<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Thymeleaf Tutorial - Thymeleaf | Docs4dev</title>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="Thymeleaf is a modern server-side Java template engine for both web and standalone environments.">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="HandheldFriendly" content="true">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta property="og:type" content="website">
<meta property="og:title" content="Thymeleaf Tutorial - Thymeleaf">
<meta property="og:url" content="https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/">
<meta property="og:site_name" content="Docs4dev">
<meta property="og:locale" content="zh_CN">
<meta name="twitter:card" content="Thymeleaf is a modern server-side Java template engine for both web and standalone environments.">
<meta name="generator" content="Docs4dev template engine">
<link rel="stylesheet" href="static/css/app.min.css">
<link rel="shortcut icon" href="https://www.docs4dev.com/static/images/favicon.ico" type="image/x-icon">
<script async="" src="static/js/js.js"></script>
<script async="" src="static/js/adsbygoogle.js" crossorigin="anonymous"></script>
<script>
    window.dataLayer = window.dataLayer || [];

    function gtag() {
      dataLayer.push(arguments);
    }

    gtag('js', new Date());
    gtag('config', 'UA-129571937-1');
  </script>
<link rel="amphtml" href="https://www.docs4dev.com/amp/docs/en/thymeleaf/3.0/reference/index.html">

<script type="application/ld+json">{"name":null,"headline":"Thymeleaf Tutorial-Thymeleaf","inLanguage":"en-US","version":"3.0","image":"/static/icon/icon-thymeleaf.png","datePublished":"2021-05-20T12:51:01Z","dateCreated":"2021-05-20T12:51:01Z","dateModified":"2021-07-03T12:24:36Z","@context":"https://schema.org/","@type":"APIReference","abstract":"Thymeleaf is a modern server-side Java template engine for both web and standalone environments."}</script>
</head>
<body>
<div class="book with-summary">
<div class="book-summary">
<div class="logo">
<a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference');" style="color: inherit;">
<img src="static/picture/icon-thymeleaf.png" style="width: 48px; height: 48px;" alt="Logo">
</a>
<b style="color: inherit; margin-left: 8px;">Thymeleaf Tutorial</b>
</div>
<div class="item">
<div>
<label for="version">版本</label>
<select id="version" onchange="onVersionChange(this)">
<option value="3.0" selected="selected">3.0</option>
</select>
</div>
<div style="margin-top: 8px;">
<label for="language">语言</label>
<select id="language" onchange="onLangChange(this)" value="en">
<option value="en" selected="selected">English</option>
<option value="zh">中文</option>
</select>
</div>

</div>
<div class="item menus">
<a title="Table of Contents" style="margin-right: 8px;" href="#">
<i class="fa fa-chevron-left"></i>
<span style="margin-left: 2px;">返回目录</span>
</a>
</div>
<nav role="navigation" id="navigation">
<ul class="summary">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#introducing-thymeleaf');" title="1 Introducing Thymeleaf"> 1 Introducing Thymeleaf </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#what-is-thymeleaf');" title="1.1 What is Thymeleaf?"> 1.1 What is Thymeleaf? </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#what-kind-of-templates-can-thymeleaf-process');" title="1.2 What kind of templates can Thymeleaf process?"> 1.2 What kind of templates can Thymeleaf process? </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#dialects-the-standard-dialect');" title="1.3 Dialects: The Standard Dialect"> 1.3 Dialects: The Standard Dialect </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#the-good-thymes-virtual-grocery');" title="2 The Good Thymes Virtual Grocery"> 2 The Good Thymes Virtual Grocery </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#a-website-for-a-grocery');" title="2.1 A website for a grocery"> 2.1 A website for a grocery </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#creating-and-configuring-the-template-engine');" title="2.2 Creating and configuring the Template Engine"> 2.2 Creating and configuring the Template Engine </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#using-texts');" title="3 Using Texts"> 3 Using Texts </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#a-multi-language-welcome');" title="3.1 A multi-language welcome"> 3.1 A multi-language welcome </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#more-on-texts-and-variables');" title="3.2 More on texts and variables"> 3.2 More on texts and variables </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#standard-expression-syntax');" title="4 Standard Expression Syntax"> 4 Standard Expression Syntax </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#messages');" title="4.1 Messages"> 4.1 Messages </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#variables');" title="4.2 Variables"> 4.2 Variables </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#expressions-on-selections-asterisk-syntax');" title="4.3 Expressions on selections (asterisk syntax)"> 4.3 Expressions on selections (asterisk syntax) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#link-urls');" title="4.4 Link URLs"> 4.4 Link URLs </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#fragments');" title="4.5 Fragments"> 4.5 Fragments </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#literals');" title="4.6 Literals"> 4.6 Literals </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#appending-texts');" title="4.7 Appending texts"> 4.7 Appending texts </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#literal-substitutions');" title="4.8 Literal substitutions"> 4.8 Literal substitutions </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#arithmetic-operations');" title="4.9 Arithmetic operations"> 4.9 Arithmetic operations </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#comparators-and-equality');" title="4.10 Comparators and Equality"> 4.10 Comparators and Equality </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#conditional-expressions');" title="4.11 Conditional expressions"> 4.11 Conditional expressions </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#default-expressions-elvis-operator');" title="4.12 Default expressions (Elvis operator)"> 4.12 Default expressions (Elvis operator) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#the-no-operation-token');" title="4.13 The No-Operation token"> 4.13 The No-Operation token </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#data-conversion-formatting');" title="4.14 Data Conversion / Formatting"> 4.14 Data Conversion / Formatting </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#preprocessing');" title="4.15 Preprocessing"> 4.15 Preprocessing </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#setting-attribute-values');" title="5 Setting Attribute Values"> 5 Setting Attribute Values </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#setting-the-value-of-any-attribute');" title="5.1 Setting the value of any attribute"> 5.1 Setting the value of any attribute </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#setting-value-to-specific-attributes');" title="5.2 Setting value to specific attributes"> 5.2 Setting value to specific attributes </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#setting-more-than-one-value-at-a-time');" title="5.3 Setting more than one value at a time"> 5.3 Setting more than one value at a time </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#appending-and-prepending');" title="5.4 Appending and prepending"> 5.4 Appending and prepending </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#fixed-value-boolean-attributes');" title="5.5 Fixed-value boolean attributes"> 5.5 Fixed-value boolean attributes </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#setting-the-value-of-any-attribute-default-attribute-processor');" title="5.6 Setting the value of any attribute (default attribute processor)"> 5.6 Setting the value of any attribute (default attribute processor) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#support-for-html5-friendly-attribute-and-element-names');" title="5.7 Support for HTML5-friendly attribute and element names"> 5.7 Support for HTML5-friendly attribute and element names </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#iteration');" title="6 Iteration"> 6 Iteration </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#iteration-basics');" title="6.1 Iteration basics"> 6.1 Iteration basics </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#keeping-iteration-status');" title="6.2 Keeping iteration status"> 6.2 Keeping iteration status </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#optimizing-through-lazy-retrieval-of-data');" title="6.3 Optimizing through lazy retrieval of data"> 6.3 Optimizing through lazy retrieval of data </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#conditional-evaluation');" title="7 Conditional Evaluation"> 7 Conditional Evaluation </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#simple-conditionals-if-and-unless');" title="7.1 Simple conditionals: “if” and “unless”"> 7.1 Simple conditionals: “if” and “unless” </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#switch-statements');" title="7.2 Switch statements"> 7.2 Switch statements </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#template-layout');" title="8 Template Layout"> 8 Template Layout </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#including-template-fragments');" title="8.1 Including template fragments"> 8.1 Including template fragments </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#parameterizable-fragment-signatures');" title="8.2 Parameterizable fragment signatures"> 8.2 Parameterizable fragment signatures </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#flexible-layouts-beyond-mere-fragment-insertion');" title="8.3 Flexible layouts: beyond mere fragment insertion"> 8.3 Flexible layouts: beyond mere fragment insertion </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#removing-template-fragments');" title="8.4 Removing template fragments"> 8.4 Removing template fragments </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#layout-inheritance');" title="8.5 Layout Inheritance"> 8.5 Layout Inheritance </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#local-variables');" title="9 Local Variables"> 9 Local Variables </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#attribute-precedence');" title="10 Attribute Precedence"> 10 Attribute Precedence </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#comments-and-blocks');" title="11 Comments and Blocks"> 11 Comments and Blocks </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#standard-htmlxml-comments');" title="11.1. Standard HTML/XML comments"> 11.1. Standard HTML/XML comments </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#thymeleaf-parser-level-comment-blocks');" title="11.2. Thymeleaf parser-level comment blocks"> 11.2. Thymeleaf parser-level comment blocks </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#thymeleaf-prototype-only-comment-blocks');" title="11.3. Thymeleaf prototype-only comment blocks"> 11.3. Thymeleaf prototype-only comment blocks </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#synthetic-thblock-tag');" title="11.4. Synthetic th:block tag"> 11.4. Synthetic th:block tag </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#inlining');" title="12 Inlining"> 12 Inlining </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#expression-inlining');" title="12.1 Expression inlining"> 12.1 Expression inlining </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#text-inlining');" title="12.2 Text inlining"> 12.2 Text inlining </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#javascript-inlining');" title="12.3 JavaScript inlining"> 12.3 JavaScript inlining </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#css-inlining');" title="12.4 CSS inlining"> 12.4 CSS inlining </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#textual-template-modes');" title="13 Textual template modes"> 13 Textual template modes </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#textual-syntax');" title="13.1 Textual syntax"> 13.1 Textual syntax </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#extensibility');" title="13.2 Extensibility"> 13.2 Extensibility </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#textual-prototype-only-comment-blocks-adding-code');" title="13.3 Textual prototype-only comment blocks: adding code"> 13.3 Textual prototype-only comment blocks: adding code </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#textual-parser-level-comment-blocks-removing-code');" title="13.4 Textual parser-level comment blocks: removing code"> 13.4 Textual parser-level comment blocks: removing code </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#natural-javascript-and-css-templates');" title="13.5 Natural JavaScript and CSS templates"> 13.5 Natural JavaScript and CSS templates </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#some-more-pages-for-our-grocery');" title="14 Some more pages for our grocery"> 14 Some more pages for our grocery </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#order-list');" title="14.1 Order List"> 14.1 Order List </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#order-details');" title="14.2 Order Details"> 14.2 Order Details </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#more-on-configuration');" title="15 More on Configuration"> 15 More on Configuration </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#template-resolvers');" title="15.1 Template Resolvers"> 15.1 Template Resolvers </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#message-resolvers');" title="15.2 Message Resolvers"> 15.2 Message Resolvers </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#conversion-services');" title="15.3 Conversion Services"> 15.3 Conversion Services </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#logging');" title="15.4 Logging"> 15.4 Logging </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#template-cache');" title="16 Template Cache"> 16 Template Cache </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#decoupled-template-logic');" title="17 Decoupled Template Logic"> 17 Decoupled Template Logic </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#decoupled-logic-the-concept');" title="17.1 Decoupled logic: The concept"> 17.1 Decoupled logic: The concept </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#configuring-decoupled-templates');" title="17.2 Configuring decoupled templates"> 17.2 Configuring decoupled templates </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#the-thref-attribute');" title="17.3 The th:ref attribute"> 17.3 The th:ref attribute </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#performance-impact-of-decoupled-templates');" title="17.4 Performance impact of decoupled templates"> 17.4 Performance impact of decoupled templates </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#resolution-of-decoupled-logic');" title="17.5 Resolution of decoupled logic"> 17.5 Resolution of decoupled logic </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#appendix-a-expression-basic-objects');" title="18 Appendix A: Expression Basic Objects"> 18 Appendix A: Expression Basic Objects </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#base-objects');" title="Base objects"> Base objects </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#web-context-namespaces-for-requestsession-attributes-etc.');" title="Web context namespaces for request/session attributes, etc."> Web context namespaces for request/session attributes, etc. </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#web-context-objects');" title="Web context objects"> Web context objects </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#appendix-b-expression-utility-objects');" title="19 Appendix B: Expression Utility Objects"> 19 Appendix B: Expression Utility Objects </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#execution-info');" title="Execution Info"> Execution Info </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#messages-1');" title="Messages"> Messages </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#urisurls');" title="URIs/URLs"> URIs/URLs </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#conversions');" title="Conversions"> Conversions </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#dates');" title="Dates"> Dates </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#calendars');" title="Calendars"> Calendars </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#numbers');" title="Numbers"> Numbers </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#strings');" title="Strings"> Strings </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#objects');" title="Objects"> Objects </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#booleans');" title="Booleans"> Booleans </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#arrays');" title="Arrays"> Arrays </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#lists');" title="Lists"> Lists </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#sets');" title="Sets"> Sets </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#maps');" title="Maps"> Maps </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#aggregates');" title="Aggregates"> Aggregates </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#ids');" title="IDs"> IDs </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#appendix-c-markup-selector-syntax');" title="20 Appendix C: Markup Selector Syntax"> 20 Appendix C: Markup Selector Syntax </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/thymeleaf/3.0/reference/using_thymeleaf.html#multivalued-class-matching');" title="Multivalued class matching"> Multivalued class matching </a> </li>
</ul> </li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<header class="book-header">
<div class="dropdown pull-right js-toolbar-action">
<a class="btn toggle-dropdown" aria-label="Language" href="#">
<i class="fa fa-globe"></i>
</a>
<div class="dropdown-menu dropdown-left">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<button class="button size-1" onclick="changeLang('zh_CN')">中文</button>
</div>
<div class="buttons">
<button class="button size-1" onclick="changeLang('en_US')">English</button>
</div>
</div>
</div>
<a class="btn pull-right js-toolbar-action non-mobile" aria-label="Sign up" href="register.html">
<i class="fa fa-sign-in"></i> <span>注册</span>
</a>
<a class="btn pull-right js-toolbar-action non-mobile" aria-label="Sign in" href="register.html">
<i class="fa fa-sign-in"></i>
<span>登录</span>
</a>
<a class="btn pull-left js-toolbar-action btn-summary" href="#"><i class="fa fa-align-justify"></i></a>
<div class="dropdown pull-left font-settings js-toolbar-action">
<a class="btn toggle-dropdown" aria-label="Font Settings" href="#">
<i class="fa fa-font"></i>
</a>
<div class="dropdown-menu dropdown-right">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<button class="button size-2 font-reduce">A</button>
<button class="button size-2 font-enlarge">A</button>
</div>
<div class="buttons">
<button class="button size-2 family-serif">Serif</button>
<button class="button size-2 family-sans">Sans</button>
</div>
<div class="buttons">
<button class="button size-3 theme-white">White</button>
<button class="button size-3 theme-sepia">Sepia</button>
<button class="button size-3 theme-night">Night</button>
</div>
</div>
</div>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Home" href="en.html">
<i class="fa fa-home"></i> <span>首页</span>
</a>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Guide" href="javascript:window.open('https://www.javadoc.org/');">
<i class="fa fa-book"></i> <span>API Docs</span>
</a>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Tools" href="index37.html">
<i class="fa fa-gears"></i> <span>工具</span>
</a>
<div class="dropdown pull-left js-toolbar-action mobile">
<a class="btn toggle-dropdown" aria-label="Language" href="#">
<i class="fa fa-chevron-down"></i>
</a>
<div class="dropdown-menu dropdown-right">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<a class="button size-1" aria-label="Home" href="en.html">
<i class="fa fa-home"></i> <span>首页</span>
</a>
</div>
<div class="buttons">
<a class="button size-1" aria-label="Guide" href="javascript:window.open('https://www.javadoc.org/');">
<i class="fa fa-book"></i> <span>API Docs</span>
</a>
</div>
<div class="buttons">
<a class="button size-1" aria-label="Tools" href="index37.html">
<i class="fa fa-gears"></i> <span>工具</span>
</a>
</div>
</div>
</div>
<div id="autocomplete" class="pull-right"></div>
<span id="toolbar-title"></span>
</header>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<section class="normal markdown-section">
<div id="content">
<h1>Thymeleaf</h1>
 <div><ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-6108808167664152" data-ad-slot="6964403648"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
<div><section id="introducing-thymeleaf" class="level1">
<h2 id="1-Introducing-Thymeleaf">1 Introducing Thymeleaf</h2>
<section id="what-is-thymeleaf" class="level2">
<h2 id="1.1-What-is-Thymeleaf?">1.1 What is Thymeleaf?</h2>
<p>Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.</p>
<p>The main goal of Thymeleaf is to provide an elegant and highly-maintainable way of creating templates. To achieve this, it builds on the concept of <em>Natural Templates</em> to inject its logic into template files in a way that doesn’t affect the template from being used as a design prototype. This improves communication of design and bridges the gap between design and development teams.</p>
<p>Thymeleaf has also been designed from the beginning with Web Standards in mind – especially <strong>HTML5</strong> – allowing you to create fully validating templates if that is a need for you.</p>
</section>
<section id="what-kind-of-templates-can-thymeleaf-process" class="level2">
<h2 id="1.2-What-kind-of-templates-can-Thymeleaf-process?">1.2 What kind of templates can Thymeleaf process?</h2>
<p>Out-of-the-box, Thymeleaf allows you to process six kinds of templates, each of which is called a <strong>Template Mode</strong>:</p>
<ul>
<li>HTML</li>
<li>XML</li>
<li>TEXT</li>
<li>JAVASCRIPT</li>
<li>CSS</li>
<li>RAW</li>
</ul>
<p>There are two <em>markup</em> template modes (<code class="notranslate">HTML</code> and <code class="notranslate">XML</code>), three <em>textual</em> template modes (<code class="notranslate">TEXT</code>, <code class="notranslate">JAVASCRIPT</code> and <code class="notranslate">CSS</code>) and a <em>no-op</em> template mode (<code class="notranslate">RAW</code>).</p>
<p>The <strong><code class="notranslate">HTML</code></strong> template mode will allow any kind of HTML input, including HTML5, HTML 4 and XHTML. No validation or well-formedness check will be performed, and template code/structure will be respected to the biggest possible extent in output.</p>
<p>The <strong><code class="notranslate">XML</code></strong> template mode will allow XML input. In this case, code is expected to be well-formed – no unclosed tags, no unquoted attributes, etc – and the parser will throw exceptions if well-formedness violations are found. Note that no <em>validation</em> (against a DTD or XML Schema) will be performed.</p>
<p>The <strong><code class="notranslate">TEXT</code></strong> template mode will allow the use of a special syntax for templates of a non-markup nature. Examples of such templates might be text emails or templated documentation. Note that HTML or XML templates can be also processed as <code class="notranslate">TEXT</code>, in which case they will not be parsed as markup, and every tag, DOCTYPE, comment, etc, will be treated as mere text.</p>
<p>The <strong><code class="notranslate">JAVASCRIPT</code></strong> template mode will allow the processing of JavaScript files in a Thymeleaf application. This means being able to use model data inside JavaScript files in the same way it can be done in HTML files, but with JavaScript-specific integrations such as specialized escaping or <em>natural scripting</em>. The <code class="notranslate">JAVASCRIPT</code> template mode is considered a <em>textual</em> mode and therefore uses the same special syntax as the <code class="notranslate">TEXT</code> template mode.</p>
<p>The <strong><code class="notranslate">CSS</code></strong> template mode will allow the processing of CSS files involved in a Thymeleaf application. Similar to the <code class="notranslate">JAVASCRIPT</code> mode, the <code class="notranslate">CSS</code> template mode is also a <em>textual</em> mode and uses the special processing syntax from the <code class="notranslate">TEXT</code> template mode.</p>
<p>The <strong><code class="notranslate">RAW</code></strong> template mode will simply not process templates at all. It is meant to be used for inserting untouched resources (files, URL responses, etc.) into the templates being processed. For example, external, uncontrolled resources in HTML format could be included into application templates, safely knowing that any Thymeleaf code that these resources might include will not be executed.</p>
</section>
<section id="dialects-the-standard-dialect" class="level2">
<h2 id="1.3-Dialects:-The-Standard-Dialect">1.3 Dialects: The Standard Dialect</h2>
<p>Thymeleaf is an extremely extensible template engine (in fact it could be called a <em>template engine framework</em>) that allows you to define and customize the way your templates will be processed to a fine level of detail.</p>
<p>An object that applies some logic to a markup artifact (a tag, some text, a comment, or a mere placeholder if templates are not markup) is called a <em>processor</em>, and a set of these processors – plus perhaps some extra artifacts – is what a <strong>dialect</strong> is normally comprised of. Out of the box, Thymeleaf’s core library provides a dialect called the <strong>Standard Dialect</strong>, which should be enough for most users.</p>
<blockquote>
<p>Note that dialects can actually have no processors and be entirely comprised of other kinds of artifacts, but processors are definitely the most common use case.</p>
</blockquote>
<p><em>This tutorial covers the Standard Dialect</em>. Every attribute and syntax feature you will learn about in the following pages is defined by this dialect, even if that isn’t explicitly mentioned.</p>
<p>Of course, users can create their own dialects (even extending the Standard one) if they want to define their own processing logic while taking advantage of the library’s advanced features. Thymeleaf can also be configured to use several dialects at a time.</p>
<blockquote>
<p>The official thymeleaf-spring3 and thymeleaf-spring4 integration packages both define a dialect called the “SpringStandard Dialect”, which is mostly the same as the Standard Dialect, but with small adaptations to make better use of some features in the Spring Framework (for example, by using Spring Expression Language or SpringEL instead of OGNL). So if you are a Spring MVC user you are not wasting your time, as almost everything you learn here will be of use in your Spring applications.</p>
</blockquote>
<p>Most of the processors of the Standard Dialect are <em>attribute processors</em>. This allows browsers to correctly display HTML template files even before being processed because they will simply ignore the additional attributes. For example, while a JSP using tag libraries could include a fragment of code not directly displayable by a browser like:</p>
<pre class="html notranslate"><code>&lt;form:inputText name="userName" value="${user.name}" /&gt;</code></pre>
<p>…the Thymeleaf Standard Dialect would allow us to achieve the same functionality with:</p>
<pre class="html notranslate"><code>&lt;input type="text" name="userName" value="James Carrot" th:value="${user.name}" /&gt;</code></pre>
<p>Not only will this be correctly displayed by browsers, but this also allow us to (optionally) specify a value attribute in it (“James Carrot”, in this case) that will be displayed when the prototype is statically opened in a browser, and that will be substituted by the value resulting from the evaluation of <code class="notranslate">${user.name}</code> during processing of the template.</p>
<p>This helps your designer and developer to work on the very same template file and reduce the effort required to transform a static prototype into a working template file. The ability to do this is a feature called <em>Natural Templating</em>.</p>
</section>
</section>
<section id="the-good-thymes-virtual-grocery" class="level1">
<h2 id="2-The-Good-Thymes-Virtual-Grocery">2 The Good Thymes Virtual Grocery</h2>
<p>The source code for the examples shown in this, and future chapters of this guide, can be found in the <a href="javascript:window.open('https://github.com/thymeleaf/thymeleafexamples-gtvg');" target="_blank" rel="noopener noreferrer">Good Thymes Virtual Grocery GitHub repository <i class="fa fa-external-link"></i></a>.</p>
<section id="a-website-for-a-grocery" class="level2">
<h2 id="2.1-A-website-for-a-grocery">2.1 A website for a grocery</h2>
<p>To better explain the concepts involved in processing templates with Thymeleaf, this tutorial will use a demo application which you can download from the project’s web site.</p>
<p>This application is the web site of an imaginary virtual grocery, and will provide us with many scenarios to showcase Thymeleaf’s many features.</p>
<p>To start, we need a simple set of model entities for our application: <code class="notranslate">Products</code> which are sold to <code class="notranslate">Customers</code> by creating <code class="notranslate">Orders</code>. We will also be managing <code class="notranslate">Comments</code> about those <code class="notranslate">Products</code>:</p>
<figure>
<img src="static/picture/gtvg-model.png" alt="Example application model">
<figcaption>
Example application model
</figcaption>
</figure>
<p>Our application will also have a very simple service layer, composed by <code class="notranslate">Service</code> objects containing methods like:</p>
<pre class="java notranslate"><code>public class ProductService {

    ...

    public List&lt;Product&gt; findAll() {
        return ProductRepository.getInstance().findAll();
    }

    public Product findById(Integer id) {
        return ProductRepository.getInstance().findById(id);
    }
    
}</code></pre>
<p>At the web layer our application will have a filter that will delegate execution to Thymeleaf-enabled commands depending on the request URL:</p>
<pre class="java notranslate"><code>private boolean process(HttpServletRequest request, HttpServletResponse response)
        throws ServletException {
    
    try {

        // This prevents triggering engine executions for resource URLs
        if (request.getRequestURI().startsWith("/css") ||
                request.getRequestURI().startsWith("/images") ||
                request.getRequestURI().startsWith("/favicon")) {
            return false;
        }

        
        /*
         * Query controller/URL mapping and obtain the controller
         * that will process the request. If no controller is available,
         * return false and let other filters/servlets process the request.
         */
        IGTVGController controller = this.application.resolveControllerForRequest(request);
        if (controller == null) {
            return false;
        }

        /*
         * Obtain the TemplateEngine instance.
         */
        ITemplateEngine templateEngine = this.application.getTemplateEngine();

        /*
         * Write the response headers
         */
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        /*
         * Execute the controller and process view template,
         * writing the results to the response writer. 
         */
        controller.process(
                request, response, this.servletContext, templateEngine);
        
        return true;
        
    } catch (Exception e) {
        try {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } catch (final IOException ignored) {
            // Just ignore this
        }
        throw new ServletException(e);
    }
    
}</code></pre>
<p>This is our <code class="notranslate">IGTVGController</code> interface:</p>
<pre class="java notranslate"><code>public interface IGTVGController {

    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, ITemplateEngine templateEngine);    
    
}</code></pre>
<p>All we have to do now is create implementations of the <code class="notranslate">IGTVGController</code> interface, retrieving data from the services and processing templates using the <code class="notranslate">ITemplateEngine</code> object.</p>
<p>In the end, it will look like this:</p>
<figure>
<img src="static/picture/gtvg-view.png" alt="Example application home page">
<figcaption>
Example application home page
</figcaption>
</figure>
<p>But first let’s see how that template engine is initialized.</p>
</section>
<section id="creating-and-configuring-the-template-engine" class="level2">
<h2 id="2.2-Creating-and-configuring-the-Template-Engine">2.2 Creating and configuring the Template Engine</h2>
<p>The <em>process(…)</em> method in our filter contained this line:</p>
<pre class="java notranslate"><code>ITemplateEngine templateEngine = this.application.getTemplateEngine();</code></pre>
<p>Which means that the <em>GTVGApplication</em> class is in charge of creating and configuring one of the most important objects in a Thymeleaf application: the <code class="notranslate">TemplateEngine</code> instance (implementation of the <code class="notranslate">ITemplateEngine</code> interface).</p>
<p>Our <code class="notranslate">org.thymeleaf.TemplateEngine</code> object is initialized like this:</p>
<pre class="java notranslate"><code>public class GTVGApplication {
  
    
    ...
    private final TemplateEngine templateEngine;
    ...
    
    
    public GTVGApplication(final ServletContext servletContext) {

        super();

        ServletContextTemplateResolver templateResolver = 
                new ServletContextTemplateResolver(servletContext);
        
        // HTML is the default mode, but we set it anyway for better understanding of code
        templateResolver.setTemplateMode(TemplateMode.HTML);
        // This will convert "home" to "/WEB-INF/templates/home.html"
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        // Template cache TTL=1h. If not set, entries would be cached until expelled
        templateResolver.setCacheTTLMs(Long.valueOf(3600000L));
        
        // Cache is set to true by default. Set to false if you want templates to
        // be automatically updated when modified.
        templateResolver.setCacheable(true);
        
        this.templateEngine = new TemplateEngine();
        this.templateEngine.setTemplateResolver(templateResolver);
        
        ...

    }

}</code></pre>
<p>There are many ways of configuring a <code class="notranslate">TemplateEngine</code> object, but for now these few lines of code will teach us enough about the steps needed.</p>
<section id="the-template-resolver" class="level3">
<h3 id="The-Template-Resolver">The Template Resolver</h3>
<p>Let’s start with the Template Resolver:</p>
<pre class="java notranslate"><code>ServletContextTemplateResolver templateResolver = 
        new ServletContextTemplateResolver(servletContext);</code></pre>
<p>Template Resolvers are objects that implement an interface from the Thymeleaf API called <code class="notranslate">org.thymeleaf.templateresolver.ITemplateResolver</code>:</p>
<pre class="java notranslate"><code>public interface ITemplateResolver {

    ...
  
    /*
     * Templates are resolved by their name (or content) and also (optionally) their 
     * owner template in case we are trying to resolve a fragment for another template.
     * Will return null if template cannot be handled by this template resolver.
     */
    public TemplateResolution resolveTemplate(
            final IEngineConfiguration configuration,
            final String ownerTemplate, final String template,
            final Map&lt;String, Object&gt; templateResolutionAttributes);
}</code></pre>
<p>These objects are in charge of determining how our templates will be accessed, and in this GTVG application, the <code class="notranslate">org.thymeleaf.templateresolver.ServletContextTemplateResolver</code> means that we are going to retrieve our template files as resources from the <em>Servlet Context</em>: an application-wide <code class="notranslate">javax.servlet.ServletContext</code> object that exists in every Java web application, and that resolves resources from the web application root.</p>
<p>But that’s not all we can say about the template resolver, because we can set some configuration parameters on it. First, the template mode:</p>
<pre class="java notranslate"><code>templateResolver.setTemplateMode(TemplateMode.HTML);</code></pre>
<p>HTML is the default template mode for <code class="notranslate">ServletContextTemplateResolver</code>, but it is good practice to establish it anyway so that our code documents clearly what is going on. </p>
<pre class="java notranslate"><code>templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");</code></pre>
<p>The <em>prefix</em> and <em>suffix</em> modify the template names that we will be passing to the engine for obtaining the real resource names to be used.</p>
<p>Using this configuration, the template name <em>“product/list”</em> would correspond to:</p>
<pre class="java notranslate"><code>servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")</code></pre>
<p>Optionally, the amount of time that a parsed template can live in the cache is configured at the Template Resolver by means of the <em>cacheTTLMs</em> property:</p>
<pre class="java notranslate"><code>templateResolver.setCacheTTLMs(3600000L);</code></pre>
<p>A template can still be expelled from cache before that TTL is reached if the max cache size is reached and it is the oldest entry currently cached.</p>
<blockquote>
<p>Cache behaviour and sizes can be defined by the user by implementing the <code class="notranslate">ICacheManager</code> interface or by modifying the <code class="notranslate">StandardCacheManager</code> object to manage the default cache.</p>
</blockquote>
<p>There is much more to learn about template resolvers, but for now let’s have a look at the creation of our Template Engine object.</p>
</section>
<section id="the-template-engine" class="level3">
<h3 id="The-Template-Engine">The Template Engine</h3>
<p>Template Engine objects are implementations of the <code class="notranslate">org.thymeleaf.ITemplateEngine</code> interface. One of these implementations is offered by the Thymeleaf core: <code class="notranslate">org.thymeleaf.TemplateEngine</code>, and we create an instance of it here:</p>
<pre class="java notranslate"><code>templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);</code></pre>
<p>Rather simple, isn’t it? All we need is to create an instance and set the Template Resolver to it.</p>
<p>A template resolver is the only <em>required</em> parameter a <code class="notranslate">TemplateEngine</code> needs, although there are many others that will be covered later (message resolvers, cache sizes, etc). For now, this is all we need.</p>
<p>Our Template Engine is now ready and we can start creating our pages using Thymeleaf.</p>
</section>
</section>
</section>
<section id="using-texts" class="level1">
<h2 id="3-Using-Texts">3 Using Texts</h2>
<section id="a-multi-language-welcome" class="level2">
<h2 id="3.1-A-multi-language-welcome">3.1 A multi-language welcome</h2>
<p>Our first task will be to create a home page for our grocery site.</p>
<p>The first version of this page will be extremely simple: just a title and a welcome message. This is our <code class="notranslate">/WEB-INF/templates/home.html</code> file:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html xmlns:th="http://www.thymeleaf.org"&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /&gt;
  &lt;/head&gt;

  &lt;body&gt;
  
    &lt;p th:text="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;
  
  &lt;/body&gt;

&lt;/html&gt;</code></pre>
<p>The first thing you will notice is that this file is HTML5 that can be correctly displayed by any browser because it does not include any non-HTML tags (browsers ignore all attributes they don’t understand, like <code class="notranslate">th:text</code>).</p>
<p>But you may also notice that this template is not really a <em>valid</em> HTML5 document, because these non-standard attributes we are using in the <code class="notranslate">th:*</code> form are not allowed by the HTML5 specification. In fact, we are even adding an <code class="notranslate">xmlns:th</code> attribute to our <code class="notranslate">&lt;html&gt;</code> tag, something absolutely non-HTML5-ish:</p>
<pre class="html notranslate"><code>&lt;html xmlns:th="http://www.thymeleaf.org"&gt;</code></pre>
<p>…which has no influence at all in template processing, but works as an <em>incantation</em> that prevents our IDE from complaining about the lack of a namespace definition for all those <code class="notranslate">th:*</code> attributes.</p>
<p>So what if we wanted to make this template <strong>HTML5-valid</strong>? Easy: switch to Thymeleaf’s data attribute syntax, using the <code class="notranslate">data-</code> prefix for attribute names and hyphen (<code class="notranslate">-</code>) separators instead of semi-colons (<code class="notranslate">:</code>):</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" /&gt;
  &lt;/head&gt;

  &lt;body&gt;
  
    &lt;p data-th-text="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;
  
  &lt;/body&gt;

&lt;/html&gt;</code></pre>
<p>Custom <code class="notranslate">data-</code> prefixed attributes are allowed by the HTML5 specification, so, with this code above, our template would be a <em>valid HTML5 document</em>.</p>
<blockquote>
<p>Both notations are completely equivalent and interchangeable, but for the sake of simplicity and compactness of the code samples, this tutorial will use the <em>namespace notation</em> (<code class="notranslate">th:*</code>). Also, the <code class="notranslate">th:*</code> notation is more general and allowed in every Thymeleaf template mode (<code class="notranslate">XML</code>, <code class="notranslate">TEXT</code>…) whereas the <code class="notranslate">data-</code> notation is only allowed in <code class="notranslate">HTML</code> mode.</p>
</blockquote>
<section id="using-thtext-and-externalizing-text" class="level3">
<h3 id="Using-th:text-and-externalizing-text">Using th:text and externalizing text</h3>
<p>Externalizing text is extracting fragments of template code out of template files so that they can be kept in separate files (typically <code class="notranslate">.properties</code> files) and that they can be easily replaced with equivalent texts written in other languages (a process called internationalization or simply <em>i18n</em>). Externalized fragments of text are usually called <em>“messages”</em>.</p>
<p>Messages always have a key that identifies them, and Thymeleaf allows you to specify that a text should correspond to a specific message with the <code class="notranslate">#{...}</code> syntax:</p>
<pre class="html notranslate"><code>&lt;p th:text="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;</code></pre>
<p>What we can see here are in fact two different features of the Thymeleaf Standard Dialect: </p>
<ul>
<li>The <code class="notranslate">th:text</code> attribute, which evaluates its value expression and sets the result as the body of the host tag, effectively replacing the “Welcome to our grocery store!” text we see in the code.</li>
<li>The <code class="notranslate">#{home.welcome}</code> expression, specified in the <em>Standard Expression Syntax</em>, instructing that the text to be used by the <code class="notranslate">th:text</code> attribute should be the message with the <code class="notranslate">home.welcome</code> key corresponding to whichever locale we are processing the template with.</li>
</ul>
<p>Now, where is this externalized text?</p>
<p>The location of externalized text in Thymeleaf is fully configurable, and it will depend on the specific <code class="notranslate">org.thymeleaf.messageresolver.IMessageResolver</code> implementation being used. Normally, an implementation based on <code class="notranslate">.properties</code> files will be used, but we could create our own implementations if we wanted, for example, to obtain messages from a database.</p>
<p>However, we have not specified a message resolver for our template engine during initialization, and that means that our application is using the <em>Standard Message Resolver</em>, implemented by <code class="notranslate">org.thymeleaf.messageresolver.StandardMessageResolver</code>.</p>
<p>The standard message resolver expects to find messages for <code class="notranslate">/WEB-INF/templates/home.html</code> in properties files in the same folder and with the same name as the template, like:</p>
<ul>
<li><code class="notranslate">/WEB-INF/templates/home_en.properties</code> for English texts.</li>
<li><code class="notranslate">/WEB-INF/templates/home_es.properties</code> for Spanish language texts.</li>
<li><code class="notranslate">/WEB-INF/templates/home_pt_BR.properties</code> for Portuguese (Brazil) language texts.</li>
<li><code class="notranslate">/WEB-INF/templates/home.properties</code> for default texts (if the locale is not matched).</li>
</ul>
<p>Let’s have a look at our <code class="notranslate">home_es.properties</code> file:</p>
<pre class="notranslate"><code>home.welcome=¡Bienvenido a nuestra tienda de comestibles!</code></pre>
<p>This is all we need for making Thymeleaf process our template. Let’s create our Home controller then.</p>
</section>
<section id="contexts" class="level3">
<h3 id="Contexts">Contexts</h3>
<p>In order to process our template, we will create a <code class="notranslate">HomeController</code> class implementing the <code class="notranslate">IGTVGController</code> interface we saw before:</p>
<pre class="java notranslate"><code>public class HomeController implements IGTVGController {

    public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {
        
        WebContext ctx = 
                new WebContext(request, response, servletContext, request.getLocale());
        
        templateEngine.process("home", ctx, response.getWriter());
        
    }

}</code></pre>
<p>The first thing we see is the creation of a <em>context</em>. A Thymeleaf context is an object implementing the <code class="notranslate">org.thymeleaf.context.IContext</code> interface. Contexts should contain all the data required for an execution of the template engine in a variables map, and also reference the locale that must be used for externalized messages.</p>
<pre class="java notranslate"><code>public interface IContext {

    public Locale getLocale();
    public boolean containsVariable(final String name);
    public Set&lt;String&gt; getVariableNames();
    public Object getVariable(final String name);
    
}</code></pre>
<p>There is a specialized extension of this interface, <code class="notranslate">org.thymeleaf.context.IWebContext</code>, meant to be used in ServletAPI-based web applications (like SpringMVC).</p>
<pre class="java notranslate"><code>public interface IWebContext extends IContext {
    
    public HttpServletRequest getRequest();
    public HttpServletResponse getResponse();
    public HttpSession getSession();
    public ServletContext getServletContext();
    
}</code></pre>
<p>The Thymeleaf core library offers an implementation of each of these interfaces:</p>
<ul>
<li><code class="notranslate">org.thymeleaf.context.Context</code> implements <code class="notranslate">IContext</code></li>
<li><code class="notranslate">org.thymeleaf.context.WebContext</code> implements <code class="notranslate">IWebContext</code></li>
</ul>
<p>And as you can see in the controller code, <code class="notranslate">WebContext</code> is the one we use. In fact we have to, because the use of a <code class="notranslate">ServletContextTemplateResolver</code> requires that we use a context implementing <code class="notranslate">IWebContext</code>.</p>
<pre class="java notranslate"><code>WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());</code></pre>
<p>Only three out of those four constructor arguments are required because the default locale for the system will be used if none is specified (although you should never let this happen in real applications).</p>
<p>There are some specialized expressions that we will be able to use to obtain the request parameters and the request, session and application attributes from the <code class="notranslate">WebContext</code> in our templates. For example:</p>
<ul>
<li><code class="notranslate">${x}</code> will return a variable <code class="notranslate">x</code> stored into the Thymeleaf context or as a <em>request attribute</em>.</li>
<li><code class="notranslate">${param.x}</code> will return a <em>request parameter</em> called <code class="notranslate">x</code> (which might be multivalued).</li>
<li><code class="notranslate">${session.x}</code> will return a <em>session attribute</em> called <code class="notranslate">x</code>.</li>
<li><code class="notranslate">${application.x}</code> will return a <em>servlet context attribute</em> called <code class="notranslate">x</code>.</li>
</ul>
</section>
<section id="executing-the-template-engine" class="level3">
<h3 id="Executing-the-template-engine">Executing the template engine</h3>
<p>With our context object ready, now we can tell the template engine to process the template (by its name) using the context, and passing it a response writer so that the response can be written to it:</p>
<pre class="java notranslate"><code>templateEngine.process("home", ctx, response.getWriter());</code></pre>
<p>Let’s see the results of this using the Spanish locale:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" /&gt;
  &lt;/head&gt;

  &lt;body&gt;
  
    &lt;p&gt;¡Bienvenido a nuestra tienda de comestibles!&lt;/p&gt;

  &lt;/body&gt;

&lt;/html&gt;</code></pre>
</section>
</section>
<section id="more-on-texts-and-variables" class="level2">
<h2 id="3.2-More-on-texts-and-variables">3.2 More on texts and variables</h2>
<section id="unescaped-text" class="level3">
<h3 id="Unescaped-Text">Unescaped Text</h3>
<p>The simplest version of our Home page seems to be ready now, but there is something we have not thought about… what if we had a message like this?</p>
<pre class="java notranslate"><code>home.welcome=Welcome to our &lt;b&gt;fantastic&lt;/b&gt; grocery store!</code></pre>
<p>If we execute this template like before, we will obtain:</p>
<pre class="html notranslate"><code>&lt;p&gt;Welcome to our &amp;lt;b&amp;gt;fantastic&amp;lt;/b&amp;gt; grocery store!&lt;/p&gt;</code></pre>
<p>Which is not exactly what we expected, because our <code class="notranslate">&lt;b&gt;</code> tag has been escaped and therefore it will be displayed in the browser.</p>
<p>This is the default behaviour of the <code class="notranslate">th:text</code> attribute. If we want Thymeleaf to respect our HTML tags and not escape them, we will have to use a different attribute: <code class="notranslate">th:utext</code> (for “unescaped text”):</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;</code></pre>
<p>This will output our message just like we wanted it:</p>
<pre class="html notranslate"><code>&lt;p&gt;Welcome to our &lt;b&gt;fantastic&lt;/b&gt; grocery store!&lt;/p&gt;</code></pre>
</section>
<section id="using-and-displaying-variables" class="level3">
<h3 id="Using-and-displaying-variables">Using and displaying variables</h3>
<p>Now let’s add some more content to our home page. For example, we may want to display the date below our welcome message, like this:</p>
<pre class="notranslate"><code>Welcome to our fantastic grocery store!

Today is: 12 july 2010</code></pre>
<p>First of all, we will have to modify our controller so that we add that date as a context variable:</p>
<pre class="java notranslate"><code>public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {
        
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
    Calendar cal = Calendar.getInstance();
        
    WebContext ctx = 
            new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("today", dateFormat.format(cal.getTime()));
        
    templateEngine.process("home", ctx, response.getWriter());
        
}</code></pre>
<p>We have added a <code class="notranslate">String</code> variable called <code class="notranslate">today</code> to our context, and now we can display it in our template:</p>
<pre class="html notranslate"><code>&lt;body&gt;

  &lt;p th:utext="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;

  &lt;p&gt;Today is: &lt;span th:text="${today}"&gt;13 February 2011&lt;/span&gt;&lt;/p&gt;
  
&lt;/body&gt;</code></pre>
<p>As you can see, we are still using the <code class="notranslate">th:text</code> attribute for the job (and that’s correct, because we want to replace the tag’s body), but the syntax is a little bit different this time and instead of a <code class="notranslate">#{...}</code> expression value, we are using a <code class="notranslate">${...}</code> one. This is a <strong>variable expression</strong>, and it contains an expression in a language called <em>OGNL (Object-Graph Navigation Language)</em> that will be executed on the context variables map we talked about before.</p>
<p>The <code class="notranslate">${today}</code> expression simply means “get the variable called today”, but these expressions could be more complex (like <code class="notranslate">${user.name}</code> for “get the variable called user, and call its <code class="notranslate">getName()</code> method”).</p>
<p>There are quite a lot of possibilities in attribute values: messages, variable expressions… and quite a lot more. The next chapter will show us what all these possibilities are.</p>
</section>
</section>
</section>
<section id="standard-expression-syntax" class="level1">
<h2 id="4-Standard-Expression-Syntax">4 Standard Expression Syntax</h2>
<p>We will take a small break in the development of our grocery virtual store to learn about one of the most important parts of the Thymeleaf Standard Dialect: the Thymeleaf Standard Expression syntax. </p>
<p>We have already seen two types of valid attribute values expressed in this syntax: message and variable expressions:</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;

&lt;p&gt;Today is: &lt;span th:text="${today}"&gt;13 february 2011&lt;/span&gt;&lt;/p&gt;</code></pre>
<p>But there are more types of expressions, and more interesting details to learn about the ones we already know. First, let’s see a quick summary of the Standard Expression features:</p>
<ul>
<li>Simple expressions:
<ul>
<li>Variable Expressions: <code class="notranslate">${...}</code></li>
<li>Selection Variable Expressions: <code class="notranslate">*{...}</code></li>
<li>Message Expressions: <code class="notranslate">#{...}</code></li>
<li>Link URL Expressions: <code class="notranslate">@{...}</code></li>
<li>Fragment Expressions: <code class="notranslate">~{...}</code></li>
</ul> </li>
<li>Literals
<ul>
<li>Text literals: <code class="notranslate">'one text'</code>, <code class="notranslate">'Another one!'</code>,…</li>
<li>Number literals: <code class="notranslate">0</code>, <code class="notranslate">34</code>, <code class="notranslate">3.0</code>, <code class="notranslate">12.3</code>,… </li>
<li>Boolean literals: <code class="notranslate">true</code>, <code class="notranslate">false</code></li>
<li>Null literal: <code class="notranslate">null</code></li>
<li>Literal tokens: <code class="notranslate">one</code>, <code class="notranslate">sometext</code>, <code class="notranslate">main</code>,…</li>
</ul> </li>
<li>Text operations:
<ul>
<li>String concatenation: <code class="notranslate">+</code></li>
<li>Literal substitutions: <code class="notranslate">|The name is ${name}|</code></li>
</ul> </li>
<li>Arithmetic operations:
<ul>
<li>Binary operators: <code class="notranslate">+</code>, <code class="notranslate">-</code>, <code class="notranslate">*</code>, <code class="notranslate">/</code>, <code class="notranslate">%</code></li>
<li>Minus sign (unary operator): <code class="notranslate">-</code></li>
</ul> </li>
<li>Boolean operations:
<ul>
<li>Binary operators: <code class="notranslate">and</code>, <code class="notranslate">or</code></li>
<li>Boolean negation (unary operator): <code class="notranslate">!</code>, <code class="notranslate">not</code></li>
</ul> </li>
<li>Comparisons and equality:
<ul>
<li>Comparators: <code class="notranslate">&gt;</code>, <code class="notranslate">&lt;</code>, <code class="notranslate">&gt;=</code>, <code class="notranslate">&lt;=</code> (<code class="notranslate">gt</code>, <code class="notranslate">lt</code>, <code class="notranslate">ge</code>, <code class="notranslate">le</code>) </li>
<li>Equality operators: <code class="notranslate">==</code>, <code class="notranslate">!=</code> (<code class="notranslate">eq</code>, <code class="notranslate">ne</code>) </li>
</ul> </li>
<li>Conditional operators:
<ul>
<li>If-then: <code class="notranslate">(if) ? (then)</code></li>
<li>If-then-else: <code class="notranslate">(if) ? (then) : (else)</code></li>
<li>Default: <code class="notranslate">(value) ?: (defaultvalue)</code></li>
</ul> </li>
<li>Special tokens:
<ul>
<li>No-Operation: <code class="notranslate">_</code></li>
</ul> </li>
</ul>
<p>All these features can be combined and nested:</p>
<pre class="html notranslate"><code>'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))</code></pre>
<section id="messages" class="level2">
<h2 id="4.1-Messages">4.1 Messages</h2>
<p>As we already know, <code class="notranslate">#{...}</code> message expressions allow us to link this:</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{home.welcome}"&gt;Welcome to our grocery store!&lt;/p&gt;</code></pre>
<p>…to this:</p>
<pre class="notranslate"><code>home.welcome=¡Bienvenido a nuestra tienda de comestibles!</code></pre>
<p>But there’s one aspect we still haven’t thought of: what happens if the message text is not completely static? What if, for example, our application knew who is the user visiting the site at any moment and we wanted to greet them by name?</p>
<pre class="html notranslate"><code>&lt;p&gt;¡Bienvenido a nuestra tienda de comestibles, John Apricot!&lt;/p&gt;</code></pre>
<p>This means we would need to add a parameter to our message. Just like this:</p>
<pre class="notranslate"><code>home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!</code></pre>
<p>Parameters are specified according to the <a href="javascript:window.open('https://docs.oracle.com/javase/10/docs/api/java/text/MessageFormat.html');" target="_blank" rel="noopener noreferrer"><code class="notranslate">java.text.MessageFormat</code> <i class="fa fa-external-link"></i></a> standard syntax, which means you can format to numbers and dates as specified in the API docs for classes in the <code class="notranslate">java.text.*</code> package.</p>
<p>In order to specify a value for our parameter, and given an HTTP session attribute called <code class="notranslate">user</code>, we could have:</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{home.welcome(${session.user.name})}"&gt;
  Welcome to our grocery store, Sebastian Pepper!
&lt;/p&gt;</code></pre>
<blockquote>
<p>Note that the use of <code class="notranslate">th:utext</code> here means that the formatted message will not be escaped. This example assumes that <code class="notranslate">user.name</code> is already escaped.</p>
</blockquote>
<p>Several parameters can be specified, separated by commas.</p>
<p>The message key itself can come from a variable:</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{${welcomeMsgKey}(${session.user.name})}"&gt;
  Welcome to our grocery store, Sebastian Pepper!
&lt;/p&gt;</code></pre>
</section>
<section id="variables" class="level2">
<h2 id="4.2-Variables">4.2 Variables</h2>
<p>We already mentioned that <code class="notranslate">${...}</code> expressions are in fact OGNL (Object-Graph Navigation Language) expressions executed on the map of variables contained in the context.</p>
<blockquote>
<p>For detailed info about OGNL syntax and features, you should read the <a href="javascript:window.open('https://commons.apache.org/ognl/');" target="_blank" rel="noopener noreferrer">OGNL Language Guide <i class="fa fa-external-link"></i></a></p>
<p>In Spring MVC-enabled applications OGNL will be replaced with <strong>SpringEL</strong>, but its syntax is very similar to that of OGNL (actually, exactly the same for most common cases).</p>
</blockquote>
<p>From OGNL’s syntax, we know that the expression in:</p>
<pre class="html notranslate"><code>&lt;p&gt;Today is: &lt;span th:text="${today}"&gt;13 february 2011&lt;/span&gt;.&lt;/p&gt;</code></pre>
<p>…is in fact equivalent to this:</p>
<pre class="java notranslate"><code>ctx.getVariable("today");</code></pre>
<p>But OGNL allows us to create quite more powerful expressions, and that’s how this:</p>
<pre class="html notranslate"><code>&lt;p th:utext="#{home.welcome(${session.user.name})}"&gt;
  Welcome to our grocery store, Sebastian Pepper!
&lt;/p&gt;</code></pre>
<p>…obtains the user name by executing:</p>
<pre class="java notranslate"><code>((User) ctx.getVariable("session").get("user")).getName();</code></pre>
<p>But getter method navigation is just one of OGNL’s features. Let’s see some more:</p>
<pre class="java notranslate"><code>/*
 * Access to properties using the point (.). Equivalent to calling property getters.
 */
${person.father.name}

/*
 * Access to properties can also be made by using brackets ([]) and writing 
 * the name of the property as a variable or between single quotes.
 */
${person['father']['name']}

/*
 * If the object is a map, both dot and bracket syntax will be equivalent to 
 * executing a call on its get(...) method.
 */
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

/*
 * Indexed access to arrays or collections is also performed with brackets, 
 * writing the index without quotes.
 */
${personsArray[0].name}

/*
 * Methods can be called, even with arguments.
 */
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}</code></pre>
<section id="expression-basic-objects" class="level3">
<h3 id="Expression-Basic-Objects">Expression Basic Objects</h3>
<p>When evaluating OGNL expressions on the context variables, some objects are made available to expressions for higher flexibility. These objects will be referenced (per OGNL standard) starting with the <code class="notranslate">#</code> symbol:</p>
<ul>
<li><code class="notranslate">#ctx</code>: the context object.</li>
<li><code class="notranslate">#vars:</code> the context variables.</li>
<li><code class="notranslate">#locale</code>: the context locale.</li>
<li><code class="notranslate">#request</code>: (only in Web Contexts) the <code class="notranslate">HttpServletRequest</code> object.</li>
<li><code class="notranslate">#response</code>: (only in Web Contexts) the <code class="notranslate">HttpServletResponse</code> object.</li>
<li><code class="notranslate">#session</code>: (only in Web Contexts) the <code class="notranslate">HttpSession</code> object.</li>
<li><code class="notranslate">#servletContext</code>: (only in Web Contexts) the <code class="notranslate">ServletContext</code> object.</li>
</ul>
<p>So we can do this:</p>
<pre class="html notranslate"><code>Established locale country: &lt;span th:text="${#locale.country}"&gt;US&lt;/span&gt;.</code></pre>
<p>You can read the full reference of these objects in <a href="#appendix-a-expression-basic-objects">Appendix A</a>.</p>
</section>
<section id="expression-utility-objects" class="level3">
<h3 id="Expression-Utility-Objects">Expression Utility Objects</h3>
<p>Besides these basic objects, Thymeleaf will offer us a set of utility objects that will help us perform common tasks in our expressions.</p>
<ul>
<li><code class="notranslate">#execInfo</code>: information about the template being processed.</li>
<li><code class="notranslate">#messages</code>: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.</li>
<li><code class="notranslate">#uris</code>: methods for escaping parts of URLs/URIs</li>
<li><code class="notranslate">#conversions</code>: methods for executing the configured <em>conversion service</em> (if any).</li>
<li><code class="notranslate">#dates</code>: methods for <code class="notranslate">java.util.Date</code> objects: formatting, component extraction, etc.</li>
<li><code class="notranslate">#calendars</code>: analogous to <code class="notranslate">#dates</code>, but for <code class="notranslate">java.util.Calendar</code> objects.</li>
<li><code class="notranslate">#numbers</code>: methods for formatting numeric objects.</li>
<li><code class="notranslate">#strings</code>: methods for <code class="notranslate">String</code> objects: contains, startsWith, prepending/appending, etc.</li>
<li><code class="notranslate">#objects</code>: methods for objects in general.</li>
<li><code class="notranslate">#bools</code>: methods for boolean evaluation.</li>
<li><code class="notranslate">#arrays</code>: methods for arrays.</li>
<li><code class="notranslate">#lists</code>: methods for lists.</li>
<li><code class="notranslate">#sets</code>: methods for sets.</li>
<li><code class="notranslate">#maps</code>: methods for maps.</li>
<li><code class="notranslate">#aggregates</code>: methods for creating aggregates on arrays or collections.</li>
<li><code class="notranslate">#ids</code>: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).</li>
</ul>
<p>You can check what functions are offered by each of these utility objects in the <a href="#appendix-b-expression-utility-objects">Appendix B</a>.</p>
</section>
<section id="reformatting-dates-in-our-home-page" class="level3">
<h3 id="Reformatting-dates-in-our-home-page">Reformatting dates in our home page</h3>
<p>Now we know about these utility objects, we could use them to change the way in which we show the date in our home page. Instead of doing this in our <code class="notranslate">HomeController</code>:</p>
<pre class="java notranslate"><code>SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());</code></pre>
<p>…we can do just this:</p>
<pre class="java notranslate"><code>WebContext ctx = 
    new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());</code></pre>
<p>…and then perform date formatting in the view layer itself:</p>
<pre class="html notranslate"><code>&lt;p&gt;
  Today is: &lt;span th:text="${#calendars.format(today,'dd MMMM yyyy')}"&gt;13 May 2011&lt;/span&gt;
&lt;/p&gt;</code></pre>
</section>
</section>
<section id="expressions-on-selections-asterisk-syntax" class="level2">
<h2 id="4.3-Expressions-on-selections-(asterisk-syntax)">4.3 Expressions on selections (asterisk syntax)</h2>
<p>Not only can variable expressions be written as <code class="notranslate">${...}</code>, but also as <code class="notranslate">*{...}</code>.</p>
<p>There is an important difference though: the asterisk syntax evaluates expressions on <em>selected objects</em> rather than on the whole context. That is, as long as there is no selected object, the dollar and the asterisk syntaxes do exactly the same.</p>
<p>And what is a selected object? The result of an expression using the <code class="notranslate">th:object</code> attribute. Let’s use one in our user profile (<code class="notranslate">userprofile.html</code>) page:</p>
<pre class="html notranslate"><code>  &lt;div th:object="${session.user}"&gt;
    &lt;p&gt;Name: &lt;span th:text="*{firstName}"&gt;Sebastian&lt;/span&gt;.&lt;/p&gt;
    &lt;p&gt;Surname: &lt;span th:text="*{lastName}"&gt;Pepper&lt;/span&gt;.&lt;/p&gt;
    &lt;p&gt;Nationality: &lt;span th:text="*{nationality}"&gt;Saturn&lt;/span&gt;.&lt;/p&gt;
  &lt;/div&gt;</code></pre>
<p>Which is exactly equivalent to:</p>
<pre class="html notranslate"><code>&lt;div&gt;
  &lt;p&gt;Name: &lt;span th:text="${session.user.firstName}"&gt;Sebastian&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Surname: &lt;span th:text="${session.user.lastName}"&gt;Pepper&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Nationality: &lt;span th:text="${session.user.nationality}"&gt;Saturn&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>Of course, dollar and asterisk syntax can be mixed:</p>
<pre class="html notranslate"><code>&lt;div th:object="${session.user}"&gt;
  &lt;p&gt;Name: &lt;span th:text="*{firstName}"&gt;Sebastian&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Surname: &lt;span th:text="${session.user.lastName}"&gt;Pepper&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Nationality: &lt;span th:text="*{nationality}"&gt;Saturn&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>When an object selection is in place, the selected object will also be available to dollar expressions as the <code class="notranslate">#object</code> expression variable:</p>
<pre class="html notranslate"><code>&lt;div th:object="${session.user}"&gt;
  &lt;p&gt;Name: &lt;span th:text="${#object.firstName}"&gt;Sebastian&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Surname: &lt;span th:text="${session.user.lastName}"&gt;Pepper&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Nationality: &lt;span th:text="*{nationality}"&gt;Saturn&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>As said, if no object selection has been performed, dollar and asterisk syntaxes are equivalent. </p>
<pre class="html notranslate"><code>&lt;div&gt;
  &lt;p&gt;Name: &lt;span th:text="*{session.user.name}"&gt;Sebastian&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Surname: &lt;span th:text="*{session.user.surname}"&gt;Pepper&lt;/span&gt;.&lt;/p&gt;
  &lt;p&gt;Nationality: &lt;span th:text="*{session.user.nationality}"&gt;Saturn&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;</code></pre>
</section>
<section id="link-urls" class="level2">
<h2 id="4.4-Link-URLs">4.4 Link URLs</h2>
<p>Because of their importance, URLs are first-class citizens in web application templates, and the <em>Thymeleaf Standard Dialect</em> has a special syntax for them, the <code class="notranslate">@</code> syntax: <code class="notranslate">@{...}</code></p>
<p>There are different types of URLs:</p>
<ul>
<li>Absolute URLs: <code class="notranslate">http://www.thymeleaf.org</code></li>
<li>Relative URLs, which can be:
<ul>
<li>Page-relative: <code class="notranslate">user/login.html</code></li>
<li>Context-relative: <code class="notranslate">/itemdetails?id=3</code> (context name in server will be added automatically)</li>
<li>Server-relative: <code class="notranslate">~/billing/processInvoice</code> (allows calling URLs in another context (= application) in the same server.</li>
<li>Protocol-relative URLs: <code class="notranslate">//code.jquery.com/jquery-2.0.3.min.js</code></li>
</ul> </li>
</ul>
<p>The real processing of these expressions and their conversion to the URLs that will be output is done by implementations of the <code class="notranslate">org.thymeleaf.linkbuilder.ILinkBuilder</code> interface that are registered into the <code class="notranslate">ITemplateEngine</code> object being used.</p>
<p>By default, a single implementation of this interface is registered of the class <code class="notranslate">org.thymeleaf.linkbuilder.StandardLinkBuilder</code>, which is enough for both offline (non-web) and also web scenarios based on the Servlet API. Other scenarios (like integration with non-ServletAPI web frameworks) might need specific implementations of the link builder interface.</p>
<p>Let’s use this new syntax. Meet the <code class="notranslate">th:href</code> attribute:</p>
<pre class="html notranslate"><code>&lt;!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) --&gt;
&lt;a href="details.html" 
   th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}"&gt;view&lt;/a&gt;

&lt;!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) --&gt;
&lt;a href="details.html" th:href="@{/order/details(orderId=${o.id})}"&gt;view&lt;/a&gt;

&lt;!-- Will produce '/gtvg/order/3/details' (plus rewriting) --&gt;
&lt;a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}"&gt;view&lt;/a&gt;</code></pre>
<p>Some things to note here:</p>
<ul>
<li><code class="notranslate">th:href</code> is a modifier attribute: once processed, it will compute the link URL to be used and set that value to the <code class="notranslate">href</code> attribute of the <code class="notranslate">&lt;a&gt;</code> tag.</li>
<li>We are allowed to use expressions for URL parameters (as you can see in <code class="notranslate">orderId=${o.id}</code>). The required URL-parameter-encoding operations will also be automatically performed.</li>
<li>If several parameters are needed, these will be separated by commas: <code class="notranslate">@{/order/process(execId=${execId},execType='FAST')}</code></li>
<li>Variable templates are also allowed in URL paths: <code class="notranslate">@{/order/{orderId}/details(orderId=${orderId})}</code></li>
<li>Relative URLs starting with <code class="notranslate">/</code> (eg: <code class="notranslate">/order/details</code>) will be automatically prefixed by the application context name.</li>
<li>If cookies are not enabled or this is not yet known, a <code class="notranslate">";jsessionid=..."</code> suffix might be added to relative URLs so that the session is preserved. This is called <em>URL Rewriting</em> and Thymeleaf allows you to plug in your own rewriting filters by using the <code class="notranslate">response.encodeURL(...)</code> mechanism from the Servlet API for every URL. </li>
<li>The <code class="notranslate">th:href</code> attribute allows us to (optionally) have a working static <code class="notranslate">href</code> attribute in our template, so that our template links remained navigable by a browser when opened directly for prototyping purposes.</li>
</ul>
<p>As was the case with the message syntax (<code class="notranslate">#{...}</code>), URL bases can also be the result of evaluating another expression:</p>
<pre class="html notranslate"><code>&lt;a th:href="@{${url}(orderId=${o.id})}"&gt;view&lt;/a&gt;
&lt;a th:href="@{'/details/'+${user.login}(orderId=${o.id})}"&gt;view&lt;/a&gt;</code></pre>
<section id="a-menu-for-our-home-page" class="level3">
<h3 id="A-menu-for-our-home-page">A menu for our home page</h3>
<p>Now that we know how to create link URLs, what about adding a small menu in our home page for some of the other pages in the site?</p>
<pre class="html notranslate"><code>&lt;p&gt;Please select an option&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href="product/list.html" th:href="@{/product/list}"&gt;Product List&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="order/list.html" th:href="@{/order/list}"&gt;Order List&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="subscribe.html" th:href="@{/subscribe}"&gt;Subscribe to our Newsletter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="userprofile.html" th:href="@{/userprofile}"&gt;See User Profile&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</code></pre>
</section>
<section id="server-root-relative-urls" class="level3">
<h3 id="Server-root-relative-URLs">Server root relative URLs</h3>
<p>An additional syntax can be used to create server-root-relative (instead of context-root-relative) URLs in order to link to different contexts in the same server. These URLs will be specified like <code class="notranslate">@{~/path/to/something}</code></p>
</section>
</section>
<section id="fragments" class="level2">
<h2 id="4.5-Fragments">4.5 Fragments</h2>
<p>Fragment expressions are an easy way to represent fragments of markup and move them around templates. This allows us to replicate them, pass them to other templates as arguments, and so on.</p>
<p>The most common use is for fragment insertion using <code class="notranslate">th:insert</code> or <code class="notranslate">th:replace</code> (more on these in a later section):</p>
<pre class="html notranslate"><code>&lt;div th:insert="~{commons :: main}"&gt;...&lt;/div&gt;</code></pre>
<p>But they can be used anywhere, just as any other variable:</p>
<pre class="html notranslate"><code>&lt;div th:with="frag=~{footer :: #main/text()}"&gt;
  &lt;p th:insert="${frag}"&gt;
&lt;/div&gt;</code></pre>
<p>Later in this tutorial there is an entire section devoted to Template Layout, including deeper explanation of fragment expressions.</p>
</section>
<section id="literals" class="level2">
<h2 id="4.6-Literals">4.6 Literals</h2>
<section id="text-literals" class="level3">
<h3 id="Text-literals">Text literals</h3>
<p>Text literals are just character strings specified between single quotes. They can include any character, but you should escape any single quotes inside them using <code class="notranslate">\'</code>. </p>
<pre class="html notranslate"><code>&lt;p&gt;
  Now you are looking at a &lt;span th:text="'working web application'"&gt;template file&lt;/span&gt;.
&lt;/p&gt;</code></pre>
</section>
<section id="number-literals" class="level3">
<h3 id="Number-literals">Number literals</h3>
<p>Numeric literals are just that: numbers.</p>
<pre class="html notranslate"><code>&lt;p&gt;The year is &lt;span th:text="2013"&gt;1492&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;In two years, it will be &lt;span th:text="2013 + 2"&gt;1494&lt;/span&gt;.&lt;/p&gt;</code></pre>
</section>
<section id="boolean-literals" class="level3">
<h3 id="Boolean-literals">Boolean literals</h3>
<p>The boolean literals are <code class="notranslate">true</code> and <code class="notranslate">false</code>. For example:</p>
<pre class="html notranslate"><code>&lt;div th:if="${user.isAdmin()} == false"&gt; ...</code></pre>
<p>In this example, the <code class="notranslate">== false</code> is written outside the braces, and so it is Thymeleaf that takes care of it. If it were written inside the braces, it would be the responsibility of the OGNL/SpringEL engines:</p>
<pre class="html notranslate"><code>&lt;div th:if="${user.isAdmin() == false}"&gt; ...</code></pre>
</section>
<section id="the-null-literal" class="level3">
<h3 id="The-null-literal">The null literal</h3>
<p>The <code class="notranslate">null</code> literal can be also used:</p>
<pre class="html notranslate"><code>&lt;div th:if="${variable.something} == null"&gt; ...</code></pre>
</section>
<section id="literal-tokens" class="level3">
<h3 id="Literal-tokens">Literal tokens</h3>
<p>Numeric, boolean and null literals are in fact a particular case of <em>literal tokens</em>. </p>
<p>These tokens allow a little bit of simplification in Standard Expressions. They work exactly the same as text literals (<code class="notranslate">'...'</code>), but they only allow letters (<code class="notranslate">A-Z</code> and <code class="notranslate">a-z</code>), numbers (<code class="notranslate">0-9</code>), brackets (<code class="notranslate">[</code> and <code class="notranslate">]</code>), dots (<code class="notranslate">.</code>), hyphens (<code class="notranslate">-</code>) and underscores (<code class="notranslate">_</code>). So no whitespaces, no commas, etc.</p>
<p>The nice part? Tokens don’t need any quotes surrounding them. So we can do this:</p>
<pre class="html notranslate"><code>&lt;div th:class="content"&gt;...&lt;/div&gt;</code></pre>
<p>instead of:</p>
<pre class="html notranslate"><code>&lt;div th:class="'content'"&gt;...&lt;/div&gt;</code></pre>
</section>
</section>
<section id="appending-texts" class="level2">
<h2 id="4.7-Appending-texts">4.7 Appending texts</h2>
<p>Texts, no matter whether they are literals or the result of evaluating variable or message expressions, can be easily appended using the <code class="notranslate">+</code> operator:</p>
<pre class="html notranslate"><code>&lt;span th:text="'The name of the user is ' + ${user.name}"&gt;</code></pre>
</section>
<section id="literal-substitutions" class="level2">
<h2 id="4.8-Literal-substitutions">4.8 Literal substitutions</h2>
<p>Literal substitutions allow for an easy formatting of strings containing values from variables without the need to append literals with <code class="notranslate">'...' + '...'</code>.</p>
<p>These substitutions must be surrounded by vertical bars (<code class="notranslate">|</code>), like:</p>
<pre class="html notranslate"><code>&lt;span th:text="|Welcome to our application, ${user.name}!|"&gt;</code></pre>
<p>Which is equivalent to:</p>
<pre class="html notranslate"><code>&lt;span th:text="'Welcome to our application, ' + ${user.name} + '!'"&gt;</code></pre>
<p>Literal substitutions can be combined with other types of expressions:</p>
<pre class="html notranslate"><code>&lt;span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|"&gt;</code></pre>
<blockquote>
<p>Only variable/message expressions (<code class="notranslate">${...}</code>, <code class="notranslate">*{...}</code>, <code class="notranslate">#{...}</code>) are allowed inside <code class="notranslate">|...|</code> literal substitutions. No other literals (<code class="notranslate">'...'</code>), boolean/numeric tokens, conditional expressions etc. are.</p>
</blockquote>
</section>
<section id="arithmetic-operations" class="level2">
<h2 id="4.9-Arithmetic-operations">4.9 Arithmetic operations</h2>
<p>Some arithmetic operations are also available: <code class="notranslate">+</code>, <code class="notranslate">-</code>, <code class="notranslate">*</code>, <code class="notranslate">/</code> and <code class="notranslate">%</code>.</p>
<pre class="html notranslate"><code>&lt;div th:with="isEven=(${prodStat.count} % 2 == 0)"&gt;</code></pre>
<p>Note that these operators can also be applied inside OGNL variable expressions themselves (and in that case will be executed by OGNL instead of the Thymeleaf Standard Expression engine):</p>
<pre class="html notranslate"><code>&lt;div th:with="isEven=${prodStat.count % 2 == 0}"&gt;</code></pre>
<p>Note that textual aliases exist for some of these operators: <code class="notranslate">div</code> (<code class="notranslate">/</code>), <code class="notranslate">mod</code> (<code class="notranslate">%</code>).</p>
</section>
<section id="comparators-and-equality" class="level2">
<h2 id="4.10-Comparators-and-Equality">4.10 Comparators and Equality</h2>
<p>Values in expressions can be compared with the <code class="notranslate">&gt;</code>, <code class="notranslate">&lt;</code>, <code class="notranslate">&gt;=</code> and <code class="notranslate">&lt;=</code> symbols, and the <code class="notranslate">==</code> and <code class="notranslate">!=</code> operators can be used to check for equality (or the lack of it). Note that XML establishes that the <code class="notranslate">&lt;</code> and <code class="notranslate">&gt;</code> symbols should not be used in attribute values, and so they should be substituted by <code class="notranslate">&amp;lt;</code> and <code class="notranslate">&amp;gt;</code>.</p>
<pre class="html notranslate"><code>&lt;div th:if="${prodStat.count} &amp;gt; 1"&gt;
&lt;span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"&gt;</code></pre>
<p>A simpler alternative may be using textual aliases that exist for some of these operators: <code class="notranslate">gt</code> (<code class="notranslate">&gt;</code>), <code class="notranslate">lt</code> (<code class="notranslate">&lt;</code>), <code class="notranslate">ge</code> (<code class="notranslate">&gt;=</code>), <code class="notranslate">le</code> (<code class="notranslate">&lt;=</code>), <code class="notranslate">not</code> (<code class="notranslate">!</code>). Also <code class="notranslate">eq</code> (<code class="notranslate">==</code>), <code class="notranslate">neq</code>/<code class="notranslate">ne</code> (<code class="notranslate">!=</code>).</p>
</section>
<section id="conditional-expressions" class="level2">
<h2 id="4.11-Conditional-expressions">4.11 Conditional expressions</h2>
<p><em>Conditional expressions</em> are meant to evaluate only one of two expressions depending on the result of evaluating a condition (which is itself another expression).</p>
<p>Let’s have a look at an example fragment (introducing another <em>attribute modifier</em>, <code class="notranslate">th:class</code>):</p>
<pre class="html notranslate"><code>&lt;tr th:class="${row.even}? 'even' : 'odd'"&gt;
  ...
&lt;/tr&gt;</code></pre>
<p>All three parts of a conditional expression (<code class="notranslate">condition</code>, <code class="notranslate">then</code> and <code class="notranslate">else</code>) are themselves expressions, which means that they can be variables (<code class="notranslate">${...}</code>, <code class="notranslate">*{...}</code>), messages (<code class="notranslate">#{...}</code>), URLs (<code class="notranslate">@{...}</code>) or literals (<code class="notranslate">'...'</code>).</p>
<p>Conditional expressions can also be nested using parentheses:</p>
<pre class="html notranslate"><code>&lt;tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'"&gt;
  ...
&lt;/tr&gt;</code></pre>
<p>Else expressions can also be omitted, in which case a null value is returned if the condition is false:</p>
<pre class="html notranslate"><code>&lt;tr th:class="${row.even}? 'alt'"&gt;
  ...
&lt;/tr&gt;</code></pre>
</section>
<section id="default-expressions-elvis-operator" class="level2">
<h2 id="4.12-Default-expressions-(Elvis-operator)">4.12 Default expressions (Elvis operator)</h2>
<p>A <em>default expression</em> is a special kind of conditional value without a <em>then</em> part. It is equivalent to the <em>Elvis operator</em> present in some languages like Groovy, lets you specify two expressions: the first one is used if it doesn’t evaluate to null, but if it does then the second one is used.</p>
<p>Let’s see it in action in our user profile page:</p>
<pre class="html notranslate"><code>&lt;div th:object="${session.user}"&gt;
  ...
  &lt;p&gt;Age: &lt;span th:text="*{age}?: '(no age specified)'"&gt;27&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>As you can see, the operator is <code class="notranslate">?:</code>, and we use it here to specify a default value for a name (a literal value, in this case) only if the result of evaluating <code class="notranslate">*{age}</code> is null. This is therefore equivalent to:</p>
<pre class="html notranslate"><code>&lt;p&gt;Age: &lt;span th:text="*{age != null}? *{age} : '(no age specified)'"&gt;27&lt;/span&gt;.&lt;/p&gt;</code></pre>
<p>As with conditional values, they can contain nested expressions between parentheses:</p>
<pre class="html notranslate"><code>&lt;p&gt;
  Name: 
  &lt;span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})"&gt;Sebastian&lt;/span&gt;
&lt;/p&gt;</code></pre>
</section>
<section id="the-no-operation-token" class="level2">
<h2 id="4.13-The-No-Operation-token">4.13 The No-Operation token</h2>
<p>The No-Operation token is represented by an underscore symbol (<code class="notranslate">_</code>).</p>
<p>The idea behind this token is to specify that the desired result for an expression is to <em>do nothing</em>, i.e.&nbsp;do exactly as if the processable attribute (e.g. <code class="notranslate">th:text</code>) was not there at all.</p>
<p>Among other possibilities, this allows developers to use prototyping text as default values. For example, instead of:</p>
<pre class="html notranslate"><code>&lt;span th:text="${user.name} ?: 'no user authenticated'"&gt;...&lt;/span&gt;</code></pre>
<p>…we can directly use <em>‘no user authenticated’</em> as a prototyping text, which results in code that is both more concise and versatile from a design standpoint:</p>
<pre class="html notranslate"><code>&lt;span th:text="${user.name} ?: _"&gt;no user authenticated&lt;/span&gt;</code></pre>
</section>
<section id="data-conversion-formatting" class="level2">
<h2 id="4.14-Data-Conversion-/-Formatting">4.14 Data Conversion / Formatting</h2>
<p>Thymeleaf defines a <em>double-brace</em> syntax for variable (<code class="notranslate">${...}</code>) and selection (<code class="notranslate">*{...}</code>) expressions that allows us to apply <em>data conversion</em> by means of a configured <em>conversion service</em>.</p>
<p>It basically goes like this:</p>
<pre class="html notranslate"><code>&lt;td th:text="${{user.lastAccessDate}}"&gt;...&lt;/td&gt;</code></pre>
<p>Noticed the double brace there?: <code class="notranslate">${{...}}</code>. That instructs Thymeleaf to pass the result of the <code class="notranslate">user.lastAccessDate</code> expression to the <em>conversion service</em> and asks it to perform a <strong>formatting operation</strong> (a conversion to <code class="notranslate">String</code>) before writing the result.</p>
<p>Assuming that <code class="notranslate">user.lastAccessDate</code> is of type <code class="notranslate">java.util.Calendar</code>, if a <em>conversion service</em> (implementation of <code class="notranslate">IStandardConversionService</code>) has been registered and contains a valid conversion for <code class="notranslate">Calendar -&gt; String</code>, it will be applied.</p>
<p>The default implementation of <code class="notranslate">IStandardConversionService</code> (the <code class="notranslate">StandardConversionService</code> class) simply executes <code class="notranslate">.toString()</code> on any object converted to <code class="notranslate">String</code>. For more information on how to register a custom <em>conversion service</em> implementation, have a look at the <a href="#more-on-configuration">More on Configuration</a> section.</p>
<blockquote>
<p>The official thymeleaf-spring3 and thymeleaf-spring4 integration packages transparently integrate Thymeleaf’s conversion service mechanism with Spring’s own <em>Conversion Service</em> infrastructure, so that conversion services and formatters declared in the Spring configuration will be made automatically available to <code class="notranslate">${{...}}</code> and <code class="notranslate">*{{...}}</code> expressions.</p>
</blockquote>
</section>
<section id="preprocessing" class="level2">
<h2 id="4.15-Preprocessing">4.15 Preprocessing</h2>
<p>In addition to all these features for expression processing, Thymeleaf has the feature of <em>preprocessing</em> expressions.</p>
<p>Preprocessing is an execution of the expressions done before the normal one that allows for modification of the expression that will eventually be executed.</p>
<p>Preprocessed expressions are exactly like normal ones, but appear surrounded by a double underscore symbol (like <code class="notranslate">__${expression}__</code>).</p>
<p>Let’s imagine we have an i18n <code class="notranslate">Messages_fr.properties</code> entry containing an OGNL expression calling a language-specific static method, like:</p>
<pre class="java notranslate"><code><a href="email-protection.html" class="__cf_email__" data-cfemail="9dfcefe9f4fef1f8b3e9f8e5e9a0ddf0e4fcededb3e9effcf3eef1fce9f2efb3c9effcf3eef1fce9f2ef">[email&#160;protected]</a>@translateToFrench({0})</code></pre>
<p>…and a <code class="notranslate">Messages_es.properties equivalent</code>:</p>
<pre class="java notranslate"><code><a href="email-protection.html" class="__cf_email__" data-cfemail="38594a4c515b545d164c5d404c05785541594848164c4a59564b54594c574a166c4a59564b54594c574a">[email&#160;protected]</a>@translateToSpanish({0})</code></pre>
<p>We can create a fragment of markup that evaluates one expression or the other depending on the locale. For this, we will first select the expression (by preprocessing) and then let Thymeleaf execute it:</p>
<pre class="html notranslate"><code>&lt;p th:text="${__#{article.text('textVar')}__}"&gt;Some text here...&lt;/p&gt;</code></pre>
<p>Note that the preprocessing step for a French locale will be creating the following equivalent: </p>
<pre class="html notranslate"><code>&lt;p th:text="${@<a href="email-protection.html" class="__cf_email__" data-cfemail="65081c0415154b1117040b160904110a174b3117040b160904110a17251117040b1609041100310a2317000b060d">[email&#160;protected]</a>(textVar)}"&gt;Some text here...&lt;/p&gt;</code></pre>
<p>The preprocessing String <code class="notranslate">__</code> can be escaped in attributes using <code class="notranslate">\_\_</code>. </p>
</section>
</section>
<section id="setting-attribute-values" class="level1">
<h2 id="5-Setting-Attribute-Values">5 Setting Attribute Values</h2>
<p>This chapter will explain the way in which we can set (or modify) values of attributes in our markup. </p>
<section id="setting-the-value-of-any-attribute" class="level2">
<h2 id="5.1-Setting-the-value-of-any-attribute">5.1 Setting the value of any attribute</h2>
<p>Say our website publishes a newsletter, and we want our users to be able to subscribe to it, so we create a <code class="notranslate">/WEB-INF/templates/subscribe.html</code> template with a form:</p>
<pre class="html notranslate"><code>&lt;form action="subscribe.html"&gt;
  &lt;fieldset&gt;
    &lt;input type="text" name="email" /&gt;
    &lt;input type="submit" value="Subscribe!" /&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;</code></pre>
<p>As with Thymeleaf, this template starts off more like a static prototype than it does a template for a web application. First, the <code class="notranslate">action</code> attribute in our form statically links to the template file itself, so that there is no place for useful URL rewriting. Second, the <code class="notranslate">value</code> attribute in the submit button makes it display a text in English, but we’d like it to be internationalized.</p>
<p>Enter then the <code class="notranslate">th:attr</code> attribute, and its ability to change the value of attributes of the tags it is set in:</p>
<pre class="html notranslate"><code>&lt;form action="subscribe.html" th:attr="<a href="email-protection.html" class="__cf_email__" data-cfemail="d2b3b1a6bbbdbcef92">[email&#160;protected]</a>{/subscribe}"&gt;
  &lt;fieldset&gt;
    &lt;input type="text" name="email" /&gt;
    &lt;input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;</code></pre>
<p>The concept is quite straightforward: <code class="notranslate">th:attr</code> simply takes an expression that assigns a value to an attribute. Having created the corresponding controller and messages files, the result of processing this file will be:</p>
<pre class="html notranslate"><code>&lt;form action="/gtvg/subscribe"&gt;
  &lt;fieldset&gt;
    &lt;input type="text" name="email" /&gt;
    &lt;input type="submit" value="¡Suscríbe!"/&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;</code></pre>
<p>Besides the new attribute values, you can also see that the application context name has been automatically prefixed to the URL base in <code class="notranslate">/gtvg/subscribe</code>, as explained in the previous chapter.</p>
<p>But what if we wanted to set more than one attribute at a time? XML rules do not allow you to set an attribute twice in a tag, so <code class="notranslate">th:attr</code> will take a comma-separated list of assignments, like:</p>
<pre class="html notranslate"><code>&lt;img src="../../images/gtvglogo.png" 
     th:attr="<a href="email-protection.html" class="__cf_email__" data-cfemail="84f7f6e7b9c4">[email&#160;protected]</a>{/images/gtvglogo.png},title=#{logo},alt=#{logo}" /&gt;</code></pre>
<p>Given the required messages files, this will output:</p>
<pre class="html notranslate"><code>&lt;img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" /&gt;</code></pre>
</section>
<section id="setting-value-to-specific-attributes" class="level2">
<h2 id="5.2-Setting-value-to-specific-attributes">5.2 Setting value to specific attributes</h2>
<p>By now, you might be thinking that something like:</p>
<pre class="html notranslate"><code>&lt;input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/&gt;</code></pre>
<p>…is quite an ugly piece of markup. Specifying an assignment inside an attribute’s value can be very practical, but it is not the most elegant way of creating templates if you have to do it all the time.</p>
<p>Thymeleaf agrees with you, and that’s why <code class="notranslate">th:attr</code> is scarcely used in templates. Normally, you will be using other <code class="notranslate">th:*</code> attributes whose task is setting specific tag attributes (and not just any attribute like <code class="notranslate">th:attr</code>).</p>
<p>For example, to set the <code class="notranslate">value</code> attribute, use <code class="notranslate">th:value</code>:</p>
<pre class="html notranslate"><code>&lt;input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/&gt;</code></pre>
<p>This looks much better! Let’s try and do the same to the <code class="notranslate">action</code> attribute in the <code class="notranslate">form</code> tag:</p>
<pre class="html notranslate"><code>&lt;form action="subscribe.html" th:action="@{/subscribe}"&gt;</code></pre>
<p>And do you remember those <code class="notranslate">th:href</code> we put in our <code class="notranslate">home.html</code> before? They are exactly this same kind of attributes:</p>
<pre class="html notranslate"><code>&lt;li&gt;&lt;a href="product/list.html" th:href="@{/product/list}"&gt;Product List&lt;/a&gt;&lt;/li&gt;</code></pre>
<p>There are quite a lot of attributes like these, each of them targeting a specific HTML5 attribute:</p>
<div class="table-scroller">
<table>
<tbody>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:abbr</code></td>
<td style="text-align: left;"><code class="notranslate">th:accept</code></td>
<td style="text-align: left;"><code class="notranslate">th:accept-charset</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:accesskey</code></td>
<td style="text-align: left;"><code class="notranslate">th:action</code></td>
<td style="text-align: left;"><code class="notranslate">th:align</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:alt</code></td>
<td style="text-align: left;"><code class="notranslate">th:archive</code></td>
<td style="text-align: left;"><code class="notranslate">th:audio</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:autocomplete</code></td>
<td style="text-align: left;"><code class="notranslate">th:axis</code></td>
<td style="text-align: left;"><code class="notranslate">th:background</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:bgcolor</code></td>
<td style="text-align: left;"><code class="notranslate">th:border</code></td>
<td style="text-align: left;"><code class="notranslate">th:cellpadding</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:cellspacing</code></td>
<td style="text-align: left;"><code class="notranslate">th:challenge</code></td>
<td style="text-align: left;"><code class="notranslate">th:charset</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:cite</code></td>
<td style="text-align: left;"><code class="notranslate">th:class</code></td>
<td style="text-align: left;"><code class="notranslate">th:classid</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:codebase</code></td>
<td style="text-align: left;"><code class="notranslate">th:codetype</code></td>
<td style="text-align: left;"><code class="notranslate">th:cols</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:colspan</code></td>
<td style="text-align: left;"><code class="notranslate">th:compact</code></td>
<td style="text-align: left;"><code class="notranslate">th:content</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:contenteditable</code></td>
<td style="text-align: left;"><code class="notranslate">th:contextmenu</code></td>
<td style="text-align: left;"><code class="notranslate">th:data</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:datetime</code></td>
<td style="text-align: left;"><code class="notranslate">th:dir</code></td>
<td style="text-align: left;"><code class="notranslate">th:draggable</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:dropzone</code></td>
<td style="text-align: left;"><code class="notranslate">th:enctype</code></td>
<td style="text-align: left;"><code class="notranslate">th:for</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:form</code></td>
<td style="text-align: left;"><code class="notranslate">th:formaction</code></td>
<td style="text-align: left;"><code class="notranslate">th:formenctype</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:formmethod</code></td>
<td style="text-align: left;"><code class="notranslate">th:formtarget</code></td>
<td style="text-align: left;"><code class="notranslate">th:fragment</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:frame</code></td>
<td style="text-align: left;"><code class="notranslate">th:frameborder</code></td>
<td style="text-align: left;"><code class="notranslate">th:headers</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:height</code></td>
<td style="text-align: left;"><code class="notranslate">th:high</code></td>
<td style="text-align: left;"><code class="notranslate">th:href</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:hreflang</code></td>
<td style="text-align: left;"><code class="notranslate">th:hspace</code></td>
<td style="text-align: left;"><code class="notranslate">th:http-equiv</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:icon</code></td>
<td style="text-align: left;"><code class="notranslate">th:id</code></td>
<td style="text-align: left;"><code class="notranslate">th:inline</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:keytype</code></td>
<td style="text-align: left;"><code class="notranslate">th:kind</code></td>
<td style="text-align: left;"><code class="notranslate">th:label</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:lang</code></td>
<td style="text-align: left;"><code class="notranslate">th:list</code></td>
<td style="text-align: left;"><code class="notranslate">th:longdesc</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:low</code></td>
<td style="text-align: left;"><code class="notranslate">th:manifest</code></td>
<td style="text-align: left;"><code class="notranslate">th:marginheight</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:marginwidth</code></td>
<td style="text-align: left;"><code class="notranslate">th:max</code></td>
<td style="text-align: left;"><code class="notranslate">th:maxlength</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:media</code></td>
<td style="text-align: left;"><code class="notranslate">th:method</code></td>
<td style="text-align: left;"><code class="notranslate">th:min</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:name</code></td>
<td style="text-align: left;"><code class="notranslate">th:onabort</code></td>
<td style="text-align: left;"><code class="notranslate">th:onafterprint</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onbeforeprint</code></td>
<td style="text-align: left;"><code class="notranslate">th:onbeforeunload</code></td>
<td style="text-align: left;"><code class="notranslate">th:onblur</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:oncanplay</code></td>
<td style="text-align: left;"><code class="notranslate">th:oncanplaythrough</code></td>
<td style="text-align: left;"><code class="notranslate">th:onchange</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onclick</code></td>
<td style="text-align: left;"><code class="notranslate">th:oncontextmenu</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondblclick</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:ondrag</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondragend</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondragenter</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:ondragleave</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondragover</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondragstart</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:ondrop</code></td>
<td style="text-align: left;"><code class="notranslate">th:ondurationchange</code></td>
<td style="text-align: left;"><code class="notranslate">th:onemptied</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onended</code></td>
<td style="text-align: left;"><code class="notranslate">th:onerror</code></td>
<td style="text-align: left;"><code class="notranslate">th:onfocus</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onformchange</code></td>
<td style="text-align: left;"><code class="notranslate">th:onforminput</code></td>
<td style="text-align: left;"><code class="notranslate">th:onhashchange</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:oninput</code></td>
<td style="text-align: left;"><code class="notranslate">th:oninvalid</code></td>
<td style="text-align: left;"><code class="notranslate">th:onkeydown</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onkeypress</code></td>
<td style="text-align: left;"><code class="notranslate">th:onkeyup</code></td>
<td style="text-align: left;"><code class="notranslate">th:onload</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onloadeddata</code></td>
<td style="text-align: left;"><code class="notranslate">th:onloadedmetadata</code></td>
<td style="text-align: left;"><code class="notranslate">th:onloadstart</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onmessage</code></td>
<td style="text-align: left;"><code class="notranslate">th:onmousedown</code></td>
<td style="text-align: left;"><code class="notranslate">th:onmousemove</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onmouseout</code></td>
<td style="text-align: left;"><code class="notranslate">th:onmouseover</code></td>
<td style="text-align: left;"><code class="notranslate">th:onmouseup</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onmousewheel</code></td>
<td style="text-align: left;"><code class="notranslate">th:onoffline</code></td>
<td style="text-align: left;"><code class="notranslate">th:ononline</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onpause</code></td>
<td style="text-align: left;"><code class="notranslate">th:onplay</code></td>
<td style="text-align: left;"><code class="notranslate">th:onplaying</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onpopstate</code></td>
<td style="text-align: left;"><code class="notranslate">th:onprogress</code></td>
<td style="text-align: left;"><code class="notranslate">th:onratechange</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onreadystatechange</code></td>
<td style="text-align: left;"><code class="notranslate">th:onredo</code></td>
<td style="text-align: left;"><code class="notranslate">th:onreset</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onresize</code></td>
<td style="text-align: left;"><code class="notranslate">th:onscroll</code></td>
<td style="text-align: left;"><code class="notranslate">th:onseeked</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onseeking</code></td>
<td style="text-align: left;"><code class="notranslate">th:onselect</code></td>
<td style="text-align: left;"><code class="notranslate">th:onshow</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onstalled</code></td>
<td style="text-align: left;"><code class="notranslate">th:onstorage</code></td>
<td style="text-align: left;"><code class="notranslate">th:onsubmit</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:onsuspend</code></td>
<td style="text-align: left;"><code class="notranslate">th:ontimeupdate</code></td>
<td style="text-align: left;"><code class="notranslate">th:onundo</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:onunload</code></td>
<td style="text-align: left;"><code class="notranslate">th:onvolumechange</code></td>
<td style="text-align: left;"><code class="notranslate">th:onwaiting</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:optimum</code></td>
<td style="text-align: left;"><code class="notranslate">th:pattern</code></td>
<td style="text-align: left;"><code class="notranslate">th:placeholder</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:poster</code></td>
<td style="text-align: left;"><code class="notranslate">th:preload</code></td>
<td style="text-align: left;"><code class="notranslate">th:radiogroup</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:rel</code></td>
<td style="text-align: left;"><code class="notranslate">th:rev</code></td>
<td style="text-align: left;"><code class="notranslate">th:rows</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:rowspan</code></td>
<td style="text-align: left;"><code class="notranslate">th:rules</code></td>
<td style="text-align: left;"><code class="notranslate">th:sandbox</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:scheme</code></td>
<td style="text-align: left;"><code class="notranslate">th:scope</code></td>
<td style="text-align: left;"><code class="notranslate">th:scrolling</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:size</code></td>
<td style="text-align: left;"><code class="notranslate">th:sizes</code></td>
<td style="text-align: left;"><code class="notranslate">th:span</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:spellcheck</code></td>
<td style="text-align: left;"><code class="notranslate">th:src</code></td>
<td style="text-align: left;"><code class="notranslate">th:srclang</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:standby</code></td>
<td style="text-align: left;"><code class="notranslate">th:start</code></td>
<td style="text-align: left;"><code class="notranslate">th:step</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:style</code></td>
<td style="text-align: left;"><code class="notranslate">th:summary</code></td>
<td style="text-align: left;"><code class="notranslate">th:tabindex</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:target</code></td>
<td style="text-align: left;"><code class="notranslate">th:title</code></td>
<td style="text-align: left;"><code class="notranslate">th:type</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:usemap</code></td>
<td style="text-align: left;"><code class="notranslate">th:value</code></td>
<td style="text-align: left;"><code class="notranslate">th:valuetype</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:vspace</code></td>
<td style="text-align: left;"><code class="notranslate">th:width</code></td>
<td style="text-align: left;"><code class="notranslate">th:wrap</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:xmlbase</code></td>
<td style="text-align: left;"><code class="notranslate">th:xmllang</code></td>
<td style="text-align: left;"><code class="notranslate">th:xmlspace</code></td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="setting-more-than-one-value-at-a-time" class="level2">
<h2 id="5.3-Setting-more-than-one-value-at-a-time">5.3 Setting more than one value at a time</h2>
<p>There are two rather special attributes called <code class="notranslate">th:alt-title</code> and <code class="notranslate">th:lang-xmllang</code> which can be used for setting two attributes to the same value at the same time. Specifically:</p>
<ul>
<li><code class="notranslate">th:alt-title</code> will set <code class="notranslate">alt</code> and <code class="notranslate">title</code>.</li>
<li><code class="notranslate">th:lang-xmllang</code> will set <code class="notranslate">lang</code> and <code class="notranslate">xml:lang</code>.</li>
</ul>
<p>For our GTVG home page, this will allow us to substitute this:</p>
<pre class="html notranslate"><code>&lt;img src="../../images/gtvglogo.png" 
     th:attr="<a href="email-protection.html" class="__cf_email__" data-cfemail="f5868796c8b5">[email&#160;protected]</a>{/images/gtvglogo.png},title=#{logo},alt=#{logo}" /&gt;</code></pre>
<p>…or this, which is equivalent:</p>
<pre class="html notranslate"><code>&lt;img src="../../images/gtvglogo.png" 
     th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" /&gt;</code></pre>
<p>…with this:</p>
<pre class="html notranslate"><code>&lt;img src="../../images/gtvglogo.png" 
     th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" /&gt;</code></pre>
</section>
<section id="appending-and-prepending" class="level2">
<h2 id="5.4-Appending-and-prepending">5.4 Appending and prepending</h2>
<p>Thymeleaf also offers the <code class="notranslate">th:attrappend</code> and <code class="notranslate">th:attrprepend</code> attributes, which append (suffix) or prepend (prefix) the result of their evaluation to the existing attribute values.</p>
<p>For example, you might want to store the name of a CSS class to be added (not set, just added) to one of your buttons in a context variable, because the specific CSS class to be used would depend on something that the user did before:</p>
<pre class="html notranslate"><code>&lt;input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" /&gt;</code></pre>
<p>If you process this template with the <code class="notranslate">cssStyle</code> variable set to <code class="notranslate">"warning"</code>, you will get:</p>
<pre class="html notranslate"><code>&lt;input type="button" value="Do it!" class="btn warning" /&gt;</code></pre>
<p>There are also two specific <em>appending attributes</em> in the Standard Dialect: the <code class="notranslate">th:classappend</code> and <code class="notranslate">th:styleappend</code> attributes, which are used for adding a CSS class or a fragment of <em>style</em> to an element without overwriting the existing ones:</p>
<pre class="html notranslate"><code>&lt;tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'"&gt;</code></pre>
<p>(Don’t worry about that <code class="notranslate">th:each</code> attribute. It is an <em>iterating attribute</em> and we will talk about it later.)</p>
</section>
<section id="fixed-value-boolean-attributes" class="level2">
<h2 id="5.5-Fixed-value-boolean-attributes">5.5 Fixed-value boolean attributes</h2>
<p>HTML has the concept of <em>boolean attributes</em>, attributes that have no value and the prescence of one means that value is “true”. In XHTML, these attributes take just 1 value, which is itself.</p>
<p>For example, <code class="notranslate">checked</code>:</p>
<pre class="html notranslate"><code>&lt;input type="checkbox" name="option2" checked /&gt; &lt;!-- HTML --&gt;
&lt;input type="checkbox" name="option1" checked="checked" /&gt; &lt;!-- XHTML --&gt;</code></pre>
<p>The Standard Dialect includes attributes that allow you to set these attributes by evaluating a condition, so that if evaluated to true, the attribute will be set to its fixed value, and if evaluated to false, the attribute will not be set:</p>
<pre class="html notranslate"><code>&lt;input type="checkbox" name="active" th:checked="${user.active}" /&gt;</code></pre>
<p>The following fixed-value boolean attributes exist in the Standard Dialect:</p>
<div class="table-scroller">
<table>
<tbody>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:async</code></td>
<td style="text-align: left;"><code class="notranslate">th:autofocus</code></td>
<td style="text-align: left;"><code class="notranslate">th:autoplay</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:checked</code></td>
<td style="text-align: left;"><code class="notranslate">th:controls</code></td>
<td style="text-align: left;"><code class="notranslate">th:declare</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:default</code></td>
<td style="text-align: left;"><code class="notranslate">th:defer</code></td>
<td style="text-align: left;"><code class="notranslate">th:disabled</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:formnovalidate</code></td>
<td style="text-align: left;"><code class="notranslate">th:hidden</code></td>
<td style="text-align: left;"><code class="notranslate">th:ismap</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:loop</code></td>
<td style="text-align: left;"><code class="notranslate">th:multiple</code></td>
<td style="text-align: left;"><code class="notranslate">th:novalidate</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:nowrap</code></td>
<td style="text-align: left;"><code class="notranslate">th:open</code></td>
<td style="text-align: left;"><code class="notranslate">th:pubdate</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code class="notranslate">th:readonly</code></td>
<td style="text-align: left;"><code class="notranslate">th:required</code></td>
<td style="text-align: left;"><code class="notranslate">th:reversed</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><code class="notranslate">th:scoped</code></td>
<td style="text-align: left;"><code class="notranslate">th:seamless</code></td>
<td style="text-align: left;"><code class="notranslate">th:selected</code></td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="setting-the-value-of-any-attribute-default-attribute-processor" class="level2">
<h2 id="5.6-Setting-the-value-of-any-attribute-(default-attribute-processor)">5.6 Setting the value of any attribute (default attribute processor)</h2>
<p>Thymeleaf offers a <em>default attribute processor</em> that allows us to set the value of <em>any</em> attribute, even if no specific <code class="notranslate">th:*</code> processor has been defined for it at the Standard Dialect.</p>
<p>So something like:</p>
<pre class="html notranslate"><code>&lt;span th:whatever="${user.name}"&gt;...&lt;/span&gt;</code></pre>
<p>Will result in:</p>
<pre class="html notranslate"><code>&lt;span whatever="John Apricot"&gt;...&lt;/span&gt;</code></pre>
</section>
<section id="support-for-html5-friendly-attribute-and-element-names" class="level2">
<h2 id="5.7-Support-for-HTML5-friendly-attribute-and-element-names">5.7 Support for HTML5-friendly attribute and element names</h2>
<p>It is also possible to use a completely different syntax to apply processors to your templates in a more HTML5-friendly manner.</p>
<pre class="html notranslate"><code>&lt;table&gt;
    &lt;tr data-th-each="user : ${users}"&gt;
        &lt;td data-th-text="${user.login}"&gt;...&lt;/td&gt;
        &lt;td data-th-text="${user.name}"&gt;...&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>The <code class="notranslate">data-{prefix}-{name}</code> syntax is the standard way to write custom attributes in HTML5, without requiring developers to use any namespaced names like <code class="notranslate">th:*</code>. Thymeleaf makes this syntax automatically available to all your dialects (not only the Standard ones).</p>
<p>There is also a syntax to specify custom tags: <code class="notranslate">{prefix}-{name}</code>, which follows the <em>W3C Custom Elements specification</em> (a part of the larger <em>W3C Web Components spec</em>). This can be used, for example, for the <code class="notranslate">th:block</code> element (or also <code class="notranslate">th-block</code>), which will be explained in a later section.</p>
<p><strong>Important:</strong> this syntax is an addition to the namespaced <code class="notranslate">th:*</code> one, it does not replace it. There is no intention at all to deprecate the namespaced syntax in the future.</p>
</section>
</section>
<section id="iteration" class="level1">
<h2 id="6-Iteration">6 Iteration</h2>
<p>So far we have created a home page, a user profile page and also a page for letting users subscribe to our newsletter… but what about our products? For that, we will need a way to iterate over items in a collection to build out our product page.</p>
<section id="iteration-basics" class="level2">
<h2 id="6.1-Iteration-basics">6.1 Iteration basics</h2>
<p>To display products in our <code class="notranslate">/WEB-INF/templates/product/list.html</code> page we will use a table. Each of our products will be displayed in a row (a <code class="notranslate">&lt;tr&gt;</code> element), and so for our template we will need to create a <em>template row</em> – one that will exemplify how we want each product to be displayed – and then instruct Thymeleaf to repeat it, once for each product.</p>
<p>The Standard Dialect offers us an attribute for exactly that: <code class="notranslate">th:each</code>.</p>
<section id="using-theach" class="level3">
<h3 id="Using-th:each">Using th:each</h3>
<p>For our product list page, we will need a controller method that retrieves the list of products from the service layer and adds it to the template context:</p>
<pre class="java notranslate"><code>public void process(
        final HttpServletRequest request, final HttpServletResponse response,
        final ServletContext servletContext, final ITemplateEngine templateEngine)
        throws Exception {
    
    ProductService productService = new ProductService();
    List&lt;Product&gt; allProducts = productService.findAll(); 
    
    WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("prods", allProducts);
    
    templateEngine.process("product/list", ctx, response.getWriter());
    
}</code></pre>
<p>And then we will use <code class="notranslate">th:each</code> in our template to iterate over the list of products:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html xmlns:th="http://www.thymeleaf.org"&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" 
          href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" /&gt;
  &lt;/head&gt;

  &lt;body&gt;

    &lt;h1&gt;Product list&lt;/h1&gt;
  
    &lt;table&gt;
      &lt;tr&gt;
        &lt;th&gt;NAME&lt;/th&gt;
        &lt;th&gt;PRICE&lt;/th&gt;
        &lt;th&gt;IN STOCK&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each="prod : ${prods}"&gt;
        &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
        &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
        &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
  
    &lt;p&gt;
      &lt;a href="../home.html" th:href="@{/}"&gt;Return to home&lt;/a&gt;
    &lt;/p&gt;

  &lt;/body&gt;

&lt;/html&gt;</code></pre>
<p>That <code class="notranslate">prod : ${prods}</code> attribute value you see above means “for each element in the result of evaluating <code class="notranslate">${prods}</code>, repeat this fragment of template, using the current element in a variable called prod”. Let’s give a name each of the things we see:</p>
<ul>
<li>We will call <code class="notranslate">${prods}</code> the <em>iterated expression</em> or <em>iterated variable</em>.</li>
<li>We will call <code class="notranslate">prod</code> the <em>iteration variable</em> or simply <em>iter variable</em>.</li>
</ul>
<p>Note that the <code class="notranslate">prod</code> iter variable is scoped to the <code class="notranslate">&lt;tr&gt;</code> element, which means it is available to inner tags like <code class="notranslate">&lt;td&gt;</code>.</p>
</section>
<section id="iterable-values" class="level3">
<h3 id="Iterable-values">Iterable values</h3>
<p>The <code class="notranslate">java.util.List</code> class isn’t the onlyvalue that can be used for iteration in Thymeleaf. There is a quite complete set of objects that are considered <em>iterable</em> by a <code class="notranslate">th:each</code> attribute:</p>
<ul>
<li>Any object implementing <code class="notranslate">java.util.Iterable</code></li>
<li>Any object implementing <code class="notranslate">java.util.Enumeration</code>.</li>
<li>Any object implementing <code class="notranslate">java.util.Iterator</code>, whose values will be used as they are returned by the iterator, without the need to cache all values in memory.</li>
<li>Any object implementing <code class="notranslate">java.util.Map</code>. When iterating maps, iter variables will be of class <code class="notranslate">java.util.Map.Entry</code>.</li>
<li>Any array.</li>
<li>Any other object will be treated as if it were a single-valued list containing the object itself.</li>
</ul>
</section>
</section>
<section id="keeping-iteration-status" class="level2">
<h2 id="6.2-Keeping-iteration-status">6.2 Keeping iteration status</h2>
<p>When using <code class="notranslate">th:each</code>, Thymeleaf offers a mechanism useful for keeping track of the status of your iteration: the <em>status variable</em>.</p>
<p>Status variables are defined within a <code class="notranslate">th:each</code> attribute and contain the following data:</p>
<ul>
<li>The current <em>iteration index</em>, starting with 0. This is the <code class="notranslate">index</code> property.</li>
<li>The current <em>iteration index</em>, starting with 1. This is the <code class="notranslate">count</code> property.</li>
<li>The total amount of elements in the iterated variable. This is the <code class="notranslate">size</code> property.</li>
<li>The <em>iter variable</em> for each iteration. This is the <code class="notranslate">current</code> property. </li>
<li>Whether the current iteration is even or odd. These are the <code class="notranslate">even/odd</code> boolean properties.</li>
<li>Whether the current iteration is the first one. This is the <code class="notranslate">first</code> boolean property.</li>
<li>Whether the current iteration is the last one. This is the <code class="notranslate">last</code> boolean property.</li>
</ul>
<p>Let’s see how we could use it with the previous example:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>The status variable (<code class="notranslate">iterStat</code> in this example) is defined in the <code class="notranslate">th:each</code> attribute by writing its name after the iter variable itself, separated by a comma. Just like the iter variable, the status variable is also scoped to the fragment of code defined by the tag holding the <code class="notranslate">th:each</code> attribute.</p>
<p>Let’s have a look at the result of processing our template:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" /&gt;
  &lt;/head&gt;

  &lt;body&gt;

    &lt;h1&gt;Product list&lt;/h1&gt;
  
    &lt;table&gt;
      &lt;tr&gt;
        &lt;th&gt;NAME&lt;/th&gt;
        &lt;th&gt;PRICE&lt;/th&gt;
        &lt;th&gt;IN STOCK&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr class="odd"&gt;
        &lt;td&gt;Fresh Sweet Basil&lt;/td&gt;
        &lt;td&gt;4.99&lt;/td&gt;
        &lt;td&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Italian Tomato&lt;/td&gt;
        &lt;td&gt;1.25&lt;/td&gt;
        &lt;td&gt;no&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr class="odd"&gt;
        &lt;td&gt;Yellow Bell Pepper&lt;/td&gt;
        &lt;td&gt;2.50&lt;/td&gt;
        &lt;td&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Old Cheddar&lt;/td&gt;
        &lt;td&gt;18.75&lt;/td&gt;
        &lt;td&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
  
    &lt;p&gt;
      &lt;a href="/gtvg/" shape="rect"&gt;Return to home&lt;/a&gt;
    &lt;/p&gt;

  &lt;/body&gt;
  
&lt;/html&gt;</code></pre>
<p>Note that our iteration status variable has worked perfectly, establishing the <code class="notranslate">odd</code> CSS class only to odd rows.</p>
<p>If you don’t explicitly set a status variable, Thymeleaf will always create one for you by suffixing <code class="notranslate">Stat</code> to the name of the iteration variable:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
</section>
<section id="optimizing-through-lazy-retrieval-of-data" class="level2">
<h2 id="6.3-Optimizing-through-lazy-retrieval-of-data">6.3 Optimizing through lazy retrieval of data</h2>
<p>Sometimes we might want to optimize the retrieval of collections of data ( e.g.&nbsp;from a database) so that these collections are only retrieved if they are really going to be used.</p>
<blockquote>
<p>Actually, this is something that can be applied to <em>any</em> piece of data, but given the size that in-memory collections might have, retrieving collections that are meant to be iterated is the most common case for this scenario.</p>
</blockquote>
<p>In order to support this, Thymeleaf offers a mechanism to <em>lazily load context variables</em>. Context variables that implement the <code class="notranslate">ILazyContextVariable</code> interface – most probably by extending its <code class="notranslate">LazyContextVariable</code> default implementation – will be resolved in the moment of being executed. For example:</p>
<pre class="java notranslate"><code>context.setVariable(
     "users",
     new LazyContextVariable&lt;List&lt;User&gt;&gt;() {
         @Override
         protected List&lt;User&gt; loadValue() {
             return databaseRepository.findAllUsers();
         }
     });</code></pre>
<p>This variable can be used without knowledge of its <em>lazyness</em>, in code such as:</p>
<pre class="html notranslate"><code>&lt;ul&gt;
  &lt;li th:each="u : ${users}" th:text="${u.name}"&gt;user name&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<p>But at the same time, will never be initialized (its <code class="notranslate">loadValue()</code> method will never be called) if <code class="notranslate">condition</code> evaluates to <code class="notranslate">false</code> in code such as:</p>
<pre class="html notranslate"><code>&lt;ul th:if="${condition}"&gt;
  &lt;li th:each="u : ${users}" th:text="${u.name}"&gt;user name&lt;/li&gt;
&lt;/ul&gt;</code></pre>
</section>
</section>
<section id="conditional-evaluation" class="level1">
<h2 id="7-Conditional-Evaluation">7 Conditional Evaluation</h2>
<section id="simple-conditionals-if-and-unless" class="level2">
<h2 id="7.1-Simple-conditionals:-“if”-and-“unless”">7.1 Simple conditionals: “if” and “unless”</h2>
<p>Sometimes you will need a fragment of your template to only appear in the result if a certain condition is met.</p>
<p>For example, imagine we want to show in our product table a column with the number of comments that exist for each product and, if there are any comments, a link to the comment detail page for that product.</p>
<p>In order to do this, we would use the <code class="notranslate">th:if</code> attribute:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span th:text="${#lists.size(prod.comments)}"&gt;2&lt;/span&gt; comment/s
      &lt;a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:if="${not #lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>Quite a lot of things to see here, so let’s focus on the important line:</p>
<pre class="html notranslate"><code>&lt;a href="comments.html"
   th:href="@{/product/comments(prodId=${prod.id})}" 
   th:if="${not #lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;</code></pre>
<p>This will create a link to the comments page (with URL <code class="notranslate">/product/comments</code>) with a <code class="notranslate">prodId</code> parameter set to the <code class="notranslate">id</code> of the product, but only if the product has any comments.</p>
<p>Let’s have a look at the resulting markup:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Fresh Sweet Basil&lt;/td&gt;
    &lt;td&gt;4.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Italian Tomato&lt;/td&gt;
    &lt;td&gt;1.25&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;2&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=2"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Yellow Bell Pepper&lt;/td&gt;
    &lt;td&gt;2.50&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Old Cheddar&lt;/td&gt;
    &lt;td&gt;18.75&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;1&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=4"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>Perfect! That’s exactly what we wanted.</p>
<p>Note that the <code class="notranslate">th:if</code> attribute will not only evaluate <em>boolean</em> conditions. Its capabilities go a little beyond that, and it will evaluate the specified expression as <code class="notranslate">true</code> following these rules:</p>
<ul>
<li>If value is not null:
<ul>
<li>If value is a boolean and is <code class="notranslate">true</code>.</li>
<li>If value is a number and is non-zero</li>
<li>If value is a character and is non-zero</li>
<li>If value is a String and is not “false”, “off” or “no”</li>
<li>If value is not a boolean, a number, a character or a String.</li>
</ul> </li>
<li>(If value is null, th:if will evaluate to false).</li>
</ul>
<p>Also, <code class="notranslate">th:if</code> has an inverse attribute, <code class="notranslate">th:unless</code>, which we could have used in the previous example instead of using a <code class="notranslate">not</code> inside the OGNL expression:</p>
<pre class="html notranslate"><code>&lt;a href="comments.html"
   th:href="@{/comments(prodId=${prod.id})}" 
   th:unless="${#lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;</code></pre>
</section>
<section id="switch-statements" class="level2">
<h2 id="7.2-Switch-statements">7.2 Switch statements</h2>
<p>There is also a way to display content conditionally using the equivalent of a <em>switch</em> structure in Java: the <code class="notranslate">th:switch</code> / <code class="notranslate">th:case</code> attribute set.</p>
<pre class="html notranslate"><code>&lt;div th:switch="${user.role}"&gt;
  &lt;p th:case="'admin'"&gt;User is an administrator&lt;/p&gt;
  &lt;p th:case="#{roles.manager}"&gt;User is a manager&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>Note that as soon as one <code class="notranslate">th:case</code> attribute is evaluated as <code class="notranslate">true</code>, every other <code class="notranslate">th:case</code> attribute in the same switch context is evaluated as <code class="notranslate">false</code>.</p>
<p>The default option is specified as <code class="notranslate">th:case="*"</code>:</p>
<pre class="html notranslate"><code>&lt;div th:switch="${user.role}"&gt;
  &lt;p th:case="'admin'"&gt;User is an administrator&lt;/p&gt;
  &lt;p th:case="#{roles.manager}"&gt;User is a manager&lt;/p&gt;
  &lt;p th:case="*"&gt;User is some other thing&lt;/p&gt;
&lt;/div&gt;</code></pre>
</section>
</section>
<section id="template-layout" class="level1">
<h2 id="8-Template-Layout">8 Template Layout</h2>
<section id="including-template-fragments" class="level2">
<h2 id="8.1-Including-template-fragments">8.1 Including template fragments</h2>
<section id="defining-and-referencing-fragments" class="level3">
<h3 id="Defining-and-referencing-fragments">Defining and referencing fragments</h3>
<p>In our templates, we will often want to include parts from other templates, parts like footers, headers, menus…</p>
<p>In order to do this, Thymeleaf needs us to define these parts, “fragments”, for inclusion, which can be done using the <code class="notranslate">th:fragment</code> attribute.</p>
<p>Say we want to add a standard copyright footer to all our grocery pages, so we create a <code class="notranslate">/WEB-INF/templates/footer.html</code> file containing this code:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html xmlns:th="http://www.thymeleaf.org"&gt;

  &lt;body&gt;
  
    &lt;div th:fragment="copy"&gt;
      &amp;copy; 2011 The Good Thymes Virtual Grocery
    &lt;/div&gt;
  
  &lt;/body&gt;
  
&lt;/html&gt;</code></pre>
<p>The code above defines a fragment called <code class="notranslate">copy</code> that we can easily include in our home page using one of the <code class="notranslate">th:insert</code> or <code class="notranslate">th:replace</code> attributes (and also <code class="notranslate">th:include</code>, though its use is no longer recommended since Thymeleaf 3.0): </p>
<pre class="html notranslate"><code>&lt;body&gt;

  ...

  &lt;div th:insert="~{footer :: copy}"&gt;&lt;/div&gt;
  
&lt;/body&gt;</code></pre>
<p>Note that <code class="notranslate">th:insert</code> expects a <em>fragment expression</em> (<code class="notranslate">~{...}</code>), which is <em>an expression that results in a fragment</em>. In the above example though, which is a non-complex <em>fragment expression</em>, the (<code class="notranslate">~{</code>,<code class="notranslate">}</code>) enclosing is completely optional, so the code above would be equivalent to:</p>
<pre class="html notranslate"><code>&lt;body&gt;

  ...

  &lt;div th:insert="footer :: copy"&gt;&lt;/div&gt;
  
&lt;/body&gt;</code></pre>
</section>
<section id="fragment-specification-syntax" class="level3">
<h3 id="Fragment-specification-syntax">Fragment specification syntax</h3>
<p>The syntax of <em>fragment expressions</em> is quite straightforward. There are three different formats:</p>
<ul>
<li> <p><code class="notranslate">"~{templatename::selector}"</code> Includes the fragment resulting from applying the specified Markup Selector on the template named <code class="notranslate">templatename</code>. Note that <code class="notranslate">selector</code> can be a mere fragment name, so you could specify something as simple as <code class="notranslate">~{templatename::fragmentname}</code> like in the <code class="notranslate">~{footer :: copy}</code> above.</p>
<blockquote>
<p>Markup Selector syntax is defined by the underlying AttoParser parsing library, and is similar to XPath expressions or CSS selectors. See <a href="#appendix-c-markup-selector-syntax">Appendix C</a> for more info.</p>
</blockquote> </li>
<li> <p><code class="notranslate">"~{templatename}"</code> Includes the complete template named <code class="notranslate">templatename</code>.</p>
<blockquote>
<p>Note that the template name you use in <code class="notranslate">th:insert</code>/<code class="notranslate">th:replace</code> tags will have to be resolvable by the Template Resolver currently being used by the Template Engine.</p>
</blockquote> </li>
<li> <p><code class="notranslate">~{::selector}"</code> or <code class="notranslate">"~{this::selector}"</code> Inserts a fragment from the same template, matching <code class="notranslate">selector</code>. If not found on the template where the expression appears, the stack of template calls (insertions) is traversed towards the originally processed template (the <em>root</em>), until <code class="notranslate">selector</code> matches at some level.</p> </li>
</ul>
<p>Both <code class="notranslate">templatename</code> and <code class="notranslate">selector</code> in the above examples can be fully-featured expressions (even conditionals!) like:</p>
<pre class="html notranslate"><code>&lt;div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"&gt;&lt;/div&gt;</code></pre>
<p>Note again how the surrounding <code class="notranslate">~{...}</code> envelope is optional in <code class="notranslate">th:insert</code>/<code class="notranslate">th:replace</code>.</p>
<p>Fragments can include any <code class="notranslate">th:*</code> attributes. These attributes will be evaluated once the fragment is included into the target template (the one with the <code class="notranslate">th:insert</code>/<code class="notranslate">th:replace</code> attribute), and they will be able to reference any context variables defined in this target template.</p>
<blockquote>
<p>A big advantage of this approach to fragments is that you can write your fragments in pages that are perfectly displayable by a browser, with a complete and even <em>valid</em> markup structure, while still retaining the ability to make Thymeleaf include them into other templates.</p>
</blockquote>
</section>
<section id="referencing-fragments-without-thfragment" class="level3">
<h3 id="Referencing-fragments-without-th:fragment">Referencing fragments without <code class="notranslate">th:fragment</code></h3>
<p>Thanks to the power of Markup Selectors, we can include fragments that do not use any <code class="notranslate">th:fragment</code> attributes. It can even be markup code coming from a different application with no knowledge of Thymeleaf at all:</p>
<pre class="html notranslate"><code>...
&lt;div id="copy-section"&gt;
  &amp;copy; 2011 The Good Thymes Virtual Grocery
&lt;/div&gt;
...</code></pre>
<p>We can use the fragment above simply referencing it by its <code class="notranslate">id</code> attribute, in a similar way to a CSS selector:</p>
<pre class="html notranslate"><code>&lt;body&gt;

  ...

  &lt;div th:insert="~{footer :: #copy-section}"&gt;&lt;/div&gt;
  
&lt;/body&gt;</code></pre>
</section>
<section id="difference-between-thinsert-and-threplace-and-thinclude" class="level3">
<h3 id="Difference-between-th:insert-and-th:replace-(and-th:include)">Difference between <code class="notranslate">th:insert</code> and <code class="notranslate">th:replace</code> (and <code class="notranslate">th:include</code>)</h3>
<p>And what is the difference between <code class="notranslate">th:insert</code> and <code class="notranslate">th:replace</code> (and <code class="notranslate">th:include</code>, not recommended since 3.0)?</p>
<ul>
<li> <p><code class="notranslate">th:insert</code> is the simplest: it will simply insert the specified fragment as the body of its host tag.</p> </li>
<li> <p><code class="notranslate">th:replace</code> actually <em>replaces</em> its host tag with the specified fragment.</p> </li>
<li> <p><code class="notranslate">th:include</code> is similar to <code class="notranslate">th:insert</code>, but instead of inserting the fragment it only inserts the <em>contents</em> of this fragment.</p> </li>
</ul>
<p>So an HTML fragment like this:</p>
<pre class="html notranslate"><code>&lt;footer th:fragment="copy"&gt;
  &amp;copy; 2011 The Good Thymes Virtual Grocery
&lt;/footer&gt;</code></pre>
<p>…included three times in host <code class="notranslate">&lt;div&gt;</code> tags, like this:</p>
<pre class="html notranslate"><code>&lt;body&gt;

  ...

  &lt;div th:insert="footer :: copy"&gt;&lt;/div&gt;

  &lt;div th:replace="footer :: copy"&gt;&lt;/div&gt;

  &lt;div th:include="footer :: copy"&gt;&lt;/div&gt;
  
&lt;/body&gt;</code></pre>
<p>…will result in:</p>
<pre class="html notranslate"><code>&lt;body&gt;

  ...

  &lt;div&gt;
    &lt;footer&gt;
      &amp;copy; 2011 The Good Thymes Virtual Grocery
    &lt;/footer&gt;
  &lt;/div&gt;

  &lt;footer&gt;
    &amp;copy; 2011 The Good Thymes Virtual Grocery
  &lt;/footer&gt;

  &lt;div&gt;
    &amp;copy; 2011 The Good Thymes Virtual Grocery
  &lt;/div&gt;
  
&lt;/body&gt;</code></pre>
</section>
</section>
<section id="parameterizable-fragment-signatures" class="level2">
<h2 id="8.2-Parameterizable-fragment-signatures">8.2 Parameterizable fragment signatures</h2>
<p>In order to create a more <em>function-like</em> mechanism for template fragments, fragments defined with <code class="notranslate">th:fragment</code> can specify a set of parameters:</p>
<pre class="html notranslate"><code>&lt;div th:fragment="frag (onevar,twovar)"&gt;
    &lt;p th:text="${onevar} + ' - ' + ${twovar}"&gt;...&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>This requires the use of one of these two syntaxes to call the fragment from <code class="notranslate">th:insert</code> or <code class="notranslate">th:replace</code>:</p>
<pre class="html notranslate"><code>&lt;div th:replace="::frag (${value1},${value2})"&gt;...&lt;/div&gt;
&lt;div th:replace="::frag (onevar=${value1},twovar=${value2})"&gt;...&lt;/div&gt;</code></pre>
<p>Note that order is not important in the last option:</p>
<pre class="html notranslate"><code>&lt;div th:replace="::frag (twovar=${value2},onevar=${value1})"&gt;...&lt;/div&gt;</code></pre>
<section id="fragment-local-variables-without-fragment-arguments" class="level3">
<h3 id="Fragment-local-variables-without-fragment-arguments">Fragment local variables without fragment arguments</h3>
<p>Even if fragments are defined without arguments like this:</p>
<pre class="html notranslate"><code>&lt;div th:fragment="frag"&gt;
    ...
&lt;/div&gt;</code></pre>
<p>We could use the second syntax specified above to call them (and only the second one):</p>
<pre class="html notranslate"><code>&lt;div th:replace="::frag (onevar=${value1},twovar=${value2})"&gt;</code></pre>
<p>This would be equivalent to a combination of <code class="notranslate">th:replace</code> and <code class="notranslate">th:with</code>:</p>
<pre class="html notranslate"><code>&lt;div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}"&gt;</code></pre>
<p><strong>Note</strong> that this specification of local variables for a fragment – no matter whether it has an argument signature or not – does not cause the context to be emptied prior to its execution. Fragments will still be able to access every context variable being used at the calling template like they currently are.</p>
</section>
<section id="thassert-for-in-template-assertions" class="level3">
<h3 id="th:assert-for-in-template-assertions">th:assert for in-template assertions</h3>
<p>The <code class="notranslate">th:assert</code> attribute can specify a comma-separated list of expressions which should be evaluated and produce true for every evaluation, raising an exception if not.</p>
<pre class="html notranslate"><code>&lt;div th:assert="${onevar},(${twovar} != 43)"&gt;...&lt;/div&gt;</code></pre>
<p>This comes in handy for validating parameters at a fragment signature:</p>
<pre class="html notranslate"><code>&lt;header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}"&gt;...&lt;/header&gt;</code></pre>
</section>
</section>
<section id="flexible-layouts-beyond-mere-fragment-insertion" class="level2">
<h2 id="8.3-Flexible-layouts:-beyond-mere-fragment-insertion">8.3 Flexible layouts: beyond mere fragment insertion</h2>
<p>Thanks to <em>fragment expressions</em>, we can specify parameters for fragments that are not texts, numbers, bean objects… but instead fragments of markup.</p>
<p>This allows us to create our fragments in a way such that they can be <em>enriched</em> with markup coming from the calling templates, resulting in a very flexible <strong>template layout mechanism</strong>.</p>
<p>Note the use of the <code class="notranslate">title</code> and <code class="notranslate">links</code> variables in the fragment below: </p>
<pre class="html notranslate"><code>&lt;head th:fragment="common_header(title,links)"&gt;

  &lt;title th:replace="${title}"&gt;The awesome application&lt;/title&gt;

  &lt;!-- Common styles and scripts --&gt;
  &lt;link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}"&gt;
  &lt;link rel="shortcut icon" th:href="@{/images/favicon.ico}"&gt;
  &lt;script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"&gt;&lt;/script&gt;

  &lt;!--/* Per-page placeholder for additional links */--&gt;
  &lt;th:block th:replace="${links}" /&gt;

&lt;/head&gt;</code></pre>
<p>We can now call this fragment like:</p>
<pre class="html notranslate"><code>...
&lt;head th:replace="base :: common_header(~{::title},~{::link})"&gt;

  &lt;title&gt;Awesome - Main&lt;/title&gt;

  &lt;link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"&gt;
  &lt;link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"&gt;

&lt;/head&gt;
...</code></pre>
<p>…and the result will use the actual <code class="notranslate">&lt;title&gt;</code> and <code class="notranslate">&lt;link&gt;</code> tags from our calling template as the values of the <code class="notranslate">title</code> and <code class="notranslate">links</code> variables, resulting in our fragment being customized during insertion:</p>
<pre class="html notranslate"><code>...
&lt;head&gt;

  &lt;title&gt;Awesome - Main&lt;/title&gt;

  &lt;!-- Common styles and scripts --&gt;
  &lt;link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"&gt;
  &lt;link rel="shortcut icon" href="/awe/images/favicon.ico"&gt;
  &lt;script type="text/javascript" src="/awe/sh/scripts/codebase.js"&gt;&lt;/script&gt;

  &lt;link rel="stylesheet" href="/awe/css/bootstrap.min.css"&gt;
  &lt;link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"&gt;

&lt;/head&gt;
...</code></pre>
<section id="using-the-empty-fragment" class="level3">
<h3 id="Using-the-empty-fragment">Using the empty fragment</h3>
<p>A special fragment expression, the <em>empty fragment</em> (<code class="notranslate">~{}</code>), can be used for specifying <em>no markup</em>. Using the previous example:</p>
<pre class="html notranslate"><code>&lt;head th:replace="base :: common_header(~{::title},~{})"&gt;

  &lt;title&gt;Awesome - Main&lt;/title&gt;

&lt;/head&gt;
...</code></pre>
<p>Note how the second parameter of the fragment (<code class="notranslate">links</code>) is set to the <em>empty fragment</em> and therefore nothing is written for the <code class="notranslate">&lt;th:block th:replace="${links}" /&gt;</code> block:</p>
<pre class="html notranslate"><code>...
&lt;head&gt;

  &lt;title&gt;Awesome - Main&lt;/title&gt;

  &lt;!-- Common styles and scripts --&gt;
  &lt;link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"&gt;
  &lt;link rel="shortcut icon" href="/awe/images/favicon.ico"&gt;
  &lt;script type="text/javascript" src="/awe/sh/scripts/codebase.js"&gt;&lt;/script&gt;

&lt;/head&gt;
...</code></pre>
</section>
<section id="using-the-no-operation-token" class="level3">
<h3 id="Using-the-no-operation-token">Using the no-operation token</h3>
<p>The no-op can be also used as a parameter to a fragment if we just want to let our fragment use its current markup as a default value. Again, using the <code class="notranslate">common_header</code> example:</p>
<pre class="html notranslate"><code>...
&lt;head th:replace="base :: common_header(_,~{::link})"&gt;

  &lt;title&gt;Awesome - Main&lt;/title&gt;

  &lt;link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"&gt;
  &lt;link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"&gt;

&lt;/head&gt;
...</code></pre>
<p>See how the <code class="notranslate">title</code> argument (first argument of the <code class="notranslate">common_header</code> fragment) is set to <em>no-op</em> (<code class="notranslate">_</code>), which results in this part of the fragment not being executed at all (<code class="notranslate">title</code> = <em>no-operation</em>):</p>
<pre class="html notranslate"><code>  &lt;title th:replace="${title}"&gt;The awesome application&lt;/title&gt;</code></pre>
<p>So the result is:</p>
<pre class="html notranslate"><code>...
&lt;head&gt;

  &lt;title&gt;The awesome application&lt;/title&gt;

  &lt;!-- Common styles and scripts --&gt;
  &lt;link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"&gt;
  &lt;link rel="shortcut icon" href="/awe/images/favicon.ico"&gt;
  &lt;script type="text/javascript" src="/awe/sh/scripts/codebase.js"&gt;&lt;/script&gt;

  &lt;link rel="stylesheet" href="/awe/css/bootstrap.min.css"&gt;
  &lt;link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"&gt;

&lt;/head&gt;
...</code></pre>
</section>
<section id="advanced-conditional-insertion-of-fragments" class="level3">
<h3 id="Advanced-conditional-insertion-of-fragments">Advanced conditional insertion of fragments</h3>
<p>The availability of both the <em>empty fragment</em> and <em>no-operation token</em> allows us to perform conditional insertion of fragments in a very easy and elegant way.</p>
<p>For example, we could do this in order to insert our <code class="notranslate">common :: adminhead</code> fragment <em>only</em> if the user is an administrator, and insert nothing (empty fragment) if not:</p>
<pre class="html notranslate"><code>...
&lt;div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}"&gt;...&lt;/div&gt;
...</code></pre>
<p>Also, we can use the <em>no-operation token</em> in order to insert a fragment only if the specified condition is met, but leave the markup without modifications if the condition is not met:</p>
<pre class="html notranslate"><code>...
&lt;div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _"&gt;
    Welcome [[${user.name}]], click &lt;a th:href="@{/support}"&gt;here&lt;/a&gt; for help-desk support.
&lt;/div&gt;
...</code></pre>
<p>Additionally, if we have configured our template resolvers to <em>check for existence</em> of the template resources –- by means of their <code class="notranslate">checkExistence</code> flag -– we can use the existence of the fragment itself as the condition in a <em>default</em> operation:</p>
<pre class="html notranslate"><code>...
&lt;!-- The body of the &lt;div&gt; will be used if the "common :: salutation" fragment  --&gt;
&lt;!-- does not exist (or is empty).                                              --&gt;
&lt;div th:insert="~{common :: salutation} ?: _"&gt;
    Welcome [[${user.name}]], click &lt;a th:href="@{/support}"&gt;here&lt;/a&gt; for help-desk support.
&lt;/div&gt;
...</code></pre>
</section>
</section>
<section id="removing-template-fragments" class="level2">
<h2 id="8.4-Removing-template-fragments">8.4 Removing template fragments</h2>
<p>Back to the example application, let’s revisit the last version of our product list template:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span th:text="${#lists.size(prod.comments)}"&gt;2&lt;/span&gt; comment/s
      &lt;a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:unless="${#lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>This code is just fine as a template, but as a static page (when directly open by a browser without Thymeleaf processing it) it would not make a nice prototype.</p>
<p>Why? Because, although perfectly displayable by browsers, that table only has a row, and this row has mock data. As a prototype, it simply wouldn’t look realistic enough… we should have more than one product, <em>we need more rows</em>.</p>
<p>So let’s add some:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span th:text="${#lists.size(prod.comments)}"&gt;2&lt;/span&gt; comment/s
      &lt;a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:unless="${#lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Blue Lettuce&lt;/td&gt;
    &lt;td&gt;9.55&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Mild Cinnamon&lt;/td&gt;
    &lt;td&gt;1.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;3&lt;/span&gt; comment/s
      &lt;a href="comments.html"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>Ok, now we have three, definitely better for a prototype. But… what will happen when we process it with Thymeleaf?:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Fresh Sweet Basil&lt;/td&gt;
    &lt;td&gt;4.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Italian Tomato&lt;/td&gt;
    &lt;td&gt;1.25&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;2&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=2"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Yellow Bell Pepper&lt;/td&gt;
    &lt;td&gt;2.50&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Old Cheddar&lt;/td&gt;
    &lt;td&gt;18.75&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;1&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=4"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Blue Lettuce&lt;/td&gt;
    &lt;td&gt;9.55&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Mild Cinnamon&lt;/td&gt;
    &lt;td&gt;1.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;3&lt;/span&gt; comment/s
      &lt;a href="comments.html"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>The last two rows are mock rows! Well, of course they are: iteration was only applied to the first row, so there is no reason why Thymeleaf should have removed the other two.</p>
<p>We need a way to remove those two rows during template processing. Let’s use the <code class="notranslate">th:remove</code> attribute on the second and third <code class="notranslate">&lt;tr&gt;</code> tags:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
    &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
    &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
    &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span th:text="${#lists.size(prod.comments)}"&gt;2&lt;/span&gt; comment/s
      &lt;a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:unless="${#lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd" th:remove="all"&gt;
    &lt;td&gt;Blue Lettuce&lt;/td&gt;
    &lt;td&gt;9.55&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr th:remove="all"&gt;
    &lt;td&gt;Mild Cinnamon&lt;/td&gt;
    &lt;td&gt;1.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;3&lt;/span&gt; comment/s
      &lt;a href="comments.html"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>Once processed, everything will look again as it should:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;NAME&lt;/th&gt;
    &lt;th&gt;PRICE&lt;/th&gt;
    &lt;th&gt;IN STOCK&lt;/th&gt;
    &lt;th&gt;COMMENTS&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Fresh Sweet Basil&lt;/td&gt;
    &lt;td&gt;4.99&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Italian Tomato&lt;/td&gt;
    &lt;td&gt;1.25&lt;/td&gt;
    &lt;td&gt;no&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;2&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=2"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Yellow Bell Pepper&lt;/td&gt;
    &lt;td&gt;2.50&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;0&lt;/span&gt; comment/s
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="odd"&gt;
    &lt;td&gt;Old Cheddar&lt;/td&gt;
    &lt;td&gt;18.75&lt;/td&gt;
    &lt;td&gt;yes&lt;/td&gt;
    &lt;td&gt;
      &lt;span&gt;1&lt;/span&gt; comment/s
      &lt;a href="/gtvg/product/comments?prodId=4"&gt;view&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</code></pre>
<p>And what does that <code class="notranslate">all</code> value in the attribute, mean? <code class="notranslate">th:remove</code> can behave in five different ways, depending on its value:</p>
<ul>
<li><code class="notranslate">all</code>: Remove both the containing tag and all its children.</li>
<li><code class="notranslate">body</code>: Do not remove the containing tag, but remove all its children.</li>
<li><code class="notranslate">tag</code>: Remove the containing tag, but do not remove its children.</li>
<li><code class="notranslate">all-but-first</code>: Remove all children of the containing tag except the first one. </li>
<li><code class="notranslate">none</code> : Do nothing. This value is useful for dynamic evaluation.</li>
</ul>
<p>What can that <code class="notranslate">all-but-first</code> value be useful for? It will let us save some <code class="notranslate">th:remove="all"</code> when prototyping:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;NAME&lt;/th&gt;
      &lt;th&gt;PRICE&lt;/th&gt;
      &lt;th&gt;IN STOCK&lt;/th&gt;
      &lt;th&gt;COMMENTS&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody th:remove="all-but-first"&gt;
    &lt;tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"&gt;
      &lt;td th:text="${prod.name}"&gt;Onions&lt;/td&gt;
      &lt;td th:text="${prod.price}"&gt;2.41&lt;/td&gt;
      &lt;td th:text="${prod.inStock}? #{true} : #{false}"&gt;yes&lt;/td&gt;
      &lt;td&gt;
        &lt;span th:text="${#lists.size(prod.comments)}"&gt;2&lt;/span&gt; comment/s
        &lt;a href="comments.html" 
           th:href="@{/product/comments(prodId=${prod.id})}" 
           th:unless="${#lists.isEmpty(prod.comments)}"&gt;view&lt;/a&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr class="odd"&gt;
      &lt;td&gt;Blue Lettuce&lt;/td&gt;
      &lt;td&gt;9.55&lt;/td&gt;
      &lt;td&gt;no&lt;/td&gt;
      &lt;td&gt;
        &lt;span&gt;0&lt;/span&gt; comment/s
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mild Cinnamon&lt;/td&gt;
      &lt;td&gt;1.99&lt;/td&gt;
      &lt;td&gt;yes&lt;/td&gt;
      &lt;td&gt;
        &lt;span&gt;3&lt;/span&gt; comment/s
        &lt;a href="comments.html"&gt;view&lt;/a&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;</code></pre>
<p>The <code class="notranslate">th:remove</code> attribute can take any <em>Thymeleaf Standard Expression</em>, as long as it returns one of the allowed String values (<code class="notranslate">all</code>, <code class="notranslate">tag</code>, <code class="notranslate">body</code>, <code class="notranslate">all-but-first</code> or <code class="notranslate">none</code>).</p>
<p>This means removals could be conditional, like:</p>
<pre class="html notranslate"><code>&lt;a href="/something" th:remove="${condition}? tag : none"&gt;Link text not to be removed&lt;/a&gt;</code></pre>
<p>Also note that <code class="notranslate">th:remove</code> considers <code class="notranslate">null</code> a synonym to <code class="notranslate">none</code>, so the following works the same as the example above:</p>
<pre class="html notranslate"><code>&lt;a href="/something" th:remove="${condition}? tag"&gt;Link text not to be removed&lt;/a&gt;</code></pre>
<p>In this case, if <code class="notranslate">${condition}</code> is false, <code class="notranslate">null</code> will be returned, and thus no removal will be performed.</p>
</section>
<section id="layout-inheritance" class="level2">
<h2 id="8.5-Layout-Inheritance">8.5 Layout Inheritance</h2>
<p>To be able to have a single file as layout, fragments can be used. An example of a simple layout having <code class="notranslate">title</code> and <code class="notranslate">content</code> using <code class="notranslate">th:fragment</code> and <code class="notranslate">th:replace</code>:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;
&lt;html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org"&gt;
&lt;head&gt;
    &lt;title th:replace="${title}"&gt;Layout Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Layout H1&lt;/h1&gt;
    &lt;div th:replace="${content}"&gt;
        &lt;p&gt;Layout content&lt;/p&gt;
    &lt;/div&gt;
    &lt;footer&gt;
        Layout footer
    &lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>This example declares a fragment called <strong>layout</strong> having <em>title</em> and <em>content</em> as parameters. Both will be replaced on page inheriting it by provided fragment expressions in the example below.</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;
&lt;html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}"&gt;
&lt;head&gt;
    &lt;title&gt;Page Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;section&gt;
    &lt;p&gt;Page content&lt;/p&gt;
    &lt;div&gt;Included on page&lt;/div&gt;
&lt;/section&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>In this file, the <code class="notranslate">html</code> tag will be replaced by <em>layout</em>, but in the layout <code class="notranslate">title</code> and <code class="notranslate">content</code> will have been replaced by <code class="notranslate">title</code> and <code class="notranslate">section</code> blocks respectively.</p>
<p>If desired, the layout can be composed by several fragments as <em>header</em> and <em>footer</em>.</p>
</section>
</section>
<section id="local-variables" class="level1">
<h2 id="9-Local-Variables">9 Local Variables</h2>
<p>Thymeleaf calls <em>local variables</em> the variables that are defined for a specific fragment of a template, and are only available for evaluation inside that fragment.</p>
<p>An example we have already seen is the <code class="notranslate">prod</code> iter variable in our product list page:</p>
<pre class="html notranslate"><code>&lt;tr th:each="prod : ${prods}"&gt;
    ...
&lt;/tr&gt;</code></pre>
<p>That <code class="notranslate">prod</code> variable will be available only within the bounds of the <code class="notranslate">&lt;tr&gt;</code> tag. Specifically:</p>
<ul>
<li>It will be available for any other <code class="notranslate">th:*</code> attributes executing in that tag with less <em>precedence</em> than <code class="notranslate">th:each</code> (which means they will execute after <code class="notranslate">th:each</code>).</li>
<li>It will be available for any child element of the <code class="notranslate">&lt;tr&gt;</code> tag, such as any <code class="notranslate">&lt;td&gt;</code> elements.</li>
</ul>
<p>Thymeleaf offers you a way to declare local variables without iteration, using the <code class="notranslate">th:with</code> attribute, and its syntax is like that of attribute value assignments:</p>
<pre class="html notranslate"><code>&lt;div th:with="firstPer=${persons[0]}"&gt;
  &lt;p&gt;
    The name of the first person is &lt;span th:text="${firstPer.name}"&gt;Julius Caesar&lt;/span&gt;.
  &lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>When <code class="notranslate">th:with</code> is processed, that <code class="notranslate">firstPer</code> variable is created as a local variable and added to the variables map coming from the context, so that it is available for evaluation along with any other variables declared in the context, but only within the bounds of the containing <code class="notranslate">&lt;div&gt;</code> tag.</p>
<p>You can define several variables at the same time using the usual multiple assignment syntax:</p>
<pre class="html notranslate"><code>&lt;div th:with="firstPer=${persons[0]},secondPer=${persons[1]}"&gt;
  &lt;p&gt;
    The name of the first person is &lt;span th:text="${firstPer.name}"&gt;Julius Caesar&lt;/span&gt;.
  &lt;/p&gt;
  &lt;p&gt;
    But the name of the second person is 
    &lt;span th:text="${secondPer.name}"&gt;Marcus Antonius&lt;/span&gt;.
  &lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>The <code class="notranslate">th:with</code> attribute allows reusing variables defined in the same attribute:</p>
<pre class="html notranslate"><code>&lt;div th:with="company=${user.company + ' Co.'},account=${accounts[company]}"&gt;...&lt;/div&gt;</code></pre>
<p>Let’s use this in our Grocery’s home page! Remember the code we wrote for outputting a formatted date?</p>
<pre class="html notranslate"><code>&lt;p&gt;
  Today is: 
  &lt;span th:text="${#calendars.format(today,'dd MMMM yyyy')}"&gt;13 february 2011&lt;/span&gt;
&lt;/p&gt;</code></pre>
<p>Well, what if we wanted that <code class="notranslate">"dd MMMM yyyy"</code> to actually depend on the locale? For example, we might want to add the following message to our <code class="notranslate">home_en.properties</code>:</p>
<pre class="notranslate"><code>date.format=MMMM dd'','' yyyy</code></pre>
<p>…and an equivalent one to our <code class="notranslate">home_es.properties</code>:</p>
<pre class="notranslate"><code>date.format=dd ''de'' MMMM'','' yyyy</code></pre>
<p>Now, let’s use <code class="notranslate">th:with</code> to get the localized date format into a variable, and then use it in our <code class="notranslate">th:text</code> expression:</p>
<pre class="html notranslate"><code>&lt;p th:with="df=#{date.format}"&gt;
  Today is: &lt;span th:text="${#calendars.format(today,df)}"&gt;13 February 2011&lt;/span&gt;
&lt;/p&gt;</code></pre>
<p>That was clean and easy. In fact, given the fact that <code class="notranslate">th:with</code> has a higher <code class="notranslate">precedence</code> than <code class="notranslate">th:text</code>, we could have solved this all in the <code class="notranslate">span</code> tag:</p>
<pre class="html notranslate"><code>&lt;p&gt;
  Today is: 
  &lt;span th:with="df=#{date.format}" 
        th:text="${#calendars.format(today,df)}"&gt;13 February 2011&lt;/span&gt;
&lt;/p&gt;</code></pre>
<p>You might be thinking: Precedence? We haven’t talked about that yet! Well, don’t worry because that is exactly what the next chapter is about.</p>
</section>
<section id="attribute-precedence" class="level1">
<h2 id="10-Attribute-Precedence">10 Attribute Precedence</h2>
<p>What happens when you write more than one <code class="notranslate">th:*</code> attribute in the same tag? For example: </p>
<pre class="html notranslate"><code>&lt;ul&gt;
  &lt;li th:each="item : ${items}" th:text="${item.description}"&gt;Item description here...&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<p>We would expect that <code class="notranslate">th:each</code> attribute to execute before the <code class="notranslate">th:text</code> so that we get the results we want, but given the fact that the HTML/XML standards do not give any kind of meaning to the order in which the attributes in a tag are written, a <em>precedence</em> mechanism had to be established in the attributes themselves in order to be sure that this will work as expected.</p>
<p>So, all Thymeleaf attributes define a numeric precedence, which establishes the order in which they are executed in the tag. This order is:</p>
<div class="table-scroller">
<table style="width:92%;">
<colgroup>
<col style="width: 11%">
<col style="width: 48%">
<col style="width: 31%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Order</th>
<th style="text-align: left;">Feature</th>
<th style="text-align: left;">Attributes</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">1</td>
<td style="text-align: left;">Fragment inclusion</td>
<td style="text-align: left;"><code class="notranslate">th:insert</code><br> <code class="notranslate">th:replace</code></td>
</tr>
<tr class="even">
<td style="text-align: left;">2</td>
<td style="text-align: left;">Fragment iteration</td>
<td style="text-align: left;"><code class="notranslate">th:each</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;">3</td>
<td style="text-align: left;">Conditional evaluation</td>
<td style="text-align: left;"><code class="notranslate">th:if</code><br> <code class="notranslate">th:unless</code><br> <code class="notranslate">th:switch</code><br> <code class="notranslate">th:case</code></td>
</tr>
<tr class="even">
<td style="text-align: left;">4</td>
<td style="text-align: left;">Local variable definition</td>
<td style="text-align: left;"><code class="notranslate">th:object</code><br> <code class="notranslate">th:with</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;">5</td>
<td style="text-align: left;">General attribute modification</td>
<td style="text-align: left;"><code class="notranslate">th:attr</code><br> <code class="notranslate">th:attrprepend</code><br> <code class="notranslate">th:attrappend</code></td>
</tr>
<tr class="even">
<td style="text-align: left;">6</td>
<td style="text-align: left;">Specific attribute modification</td>
<td style="text-align: left;"><code class="notranslate">th:value</code><br> <code class="notranslate">th:href</code><br> <code class="notranslate">th:src</code><br> <code class="notranslate">...</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;">7</td>
<td style="text-align: left;">Text (tag body modification)</td>
<td style="text-align: left;"><code class="notranslate">th:text</code><br> <code class="notranslate">th:utext</code></td>
</tr>
<tr class="even">
<td style="text-align: left;">8</td>
<td style="text-align: left;">Fragment specification</td>
<td style="text-align: left;"><code class="notranslate">th:fragment</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;">9</td>
<td style="text-align: left;">Fragment removal</td>
<td style="text-align: left;"><code class="notranslate">th:remove</code></td>
</tr>
</tbody>
</table>
</div>
<p>This precedence mechanism means that the above iteration fragment will give exactly the same results if the attribute position is inverted (although it would be slightly less readable):</p>
<pre class="html notranslate"><code>&lt;ul&gt;
  &lt;li th:text="${item.description}" th:each="item : ${items}"&gt;Item description here...&lt;/li&gt;
&lt;/ul&gt;</code></pre>
</section>
<section id="comments-and-blocks" class="level1">
<h2 id="11-Comments-and-Blocks">11 Comments and Blocks</h2>
<section id="standard-htmlxml-comments" class="level2">
<h2 id="11.1.-Standard-HTML/XML-comments">11.1. Standard HTML/XML comments</h2>
<p>Standard HTML/XML comments <code class="notranslate">&lt;!-- ... --&gt;</code> can be used anywhere in Thymeleaf templates. Anything inside these comments won’t be processed by Thymeleaf, and will be copied verbatim to the result:</p>
<pre class="html notranslate"><code>&lt;!-- User info follows --&gt;
&lt;div th:text="${...}"&gt;
  ...
&lt;/div&gt;</code></pre>
</section>
<section id="thymeleaf-parser-level-comment-blocks" class="level2">
<h2 id="11.2.-Thymeleaf-parser-level-comment-blocks">11.2. Thymeleaf parser-level comment blocks</h2>
<p>Parser-level comment blocks are code that will be simply removed from the template when Thymeleaf parses it. They look like this:</p>
<pre class="html notranslate"><code>&lt;!--/* This code will be removed at Thymeleaf parsing time! */--&gt;</code></pre>
<p>Thymeleaf will remove everything between <code class="notranslate">&lt;!--/*</code> and <code class="notranslate">*/--&gt;</code>, so these comment blocks can also be used for displaying code when a template is statically open, knowing that it will be removed when Thymeleaf processes it:</p>
<pre class="html notranslate"><code>&lt;!--/*--&gt; 
  &lt;div&gt;
     you can see me only before Thymeleaf processes me!
  &lt;/div&gt;
&lt;!--*/--&gt;</code></pre>
<p>This might come very handy for prototyping tables with a lot of <code class="notranslate">&lt;tr&gt;</code>’s, for example:</p>
<pre class="html notranslate"><code>&lt;table&gt;
   &lt;tr th:each="x : ${xs}"&gt;
     ...
   &lt;/tr&gt;
   &lt;!--/*--&gt;
   &lt;tr&gt;
     ...
   &lt;/tr&gt;
   &lt;tr&gt;
     ...
   &lt;/tr&gt;
   &lt;!--*/--&gt;
&lt;/table&gt;</code></pre>
</section>
<section id="thymeleaf-prototype-only-comment-blocks" class="level2">
<h2 id="11.3.-Thymeleaf-prototype-only-comment-blocks">11.3. Thymeleaf prototype-only comment blocks</h2>
<p>Thymeleaf allows the definition of special comment blocks marked to be comments when the template is open statically (i.e.&nbsp;as a prototype), but considered normal markup by Thymeleaf when executing the template.</p>
<pre class="html notranslate"><code>&lt;span&gt;hello!&lt;/span&gt;
&lt;!--/*/
  &lt;div th:text="${...}"&gt;
    ...
  &lt;/div&gt;
/*/--&gt;
&lt;span&gt;goodbye!&lt;/span&gt;</code></pre>
<p>Thymeleaf’s parsing system will simply remove the <code class="notranslate">&lt;!--/*/</code> and <code class="notranslate">/*/--&gt;</code> markers, but not its contents, which will be left therefore uncommented. So when executing the template, Thymeleaf will actually see this:</p>
<pre class="html notranslate"><code>&lt;span&gt;hello!&lt;/span&gt;
 
  &lt;div th:text="${...}"&gt;
    ...
  &lt;/div&gt;
 
&lt;span&gt;goodbye!&lt;/span&gt;</code></pre>
<p>As with parser-level comment blocks, this feature is dialect-independent.</p>
</section>
<section id="synthetic-thblock-tag" class="level2">
<h2 id="11.4.-Synthetic-th:block-tag">11.4. Synthetic <code class="notranslate">th:block</code> tag</h2>
<p>Thymeleaf’s only element processor (not an attribute) included in the Standard Dialects is <code class="notranslate">th:block</code>.</p>
<p><code class="notranslate">th:block</code> is a mere attribute container that allows template developers to specify whichever attributes they want. Thymeleaf will execute these attributes and then simply make the block, but not its contents, disappear.</p>
<p>So it could be useful, for example, when creating iterated tables that require more than one <code class="notranslate">&lt;tr&gt;</code> for each element:</p>
<pre class="html notranslate"><code>&lt;table&gt;
  &lt;th:block th:each="user : ${users}"&gt;
    &lt;tr&gt;
        &lt;td th:text="${user.login}"&gt;...&lt;/td&gt;
        &lt;td th:text="${user.name}"&gt;...&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td colspan="2" th:text="${user.address}"&gt;...&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/th:block&gt;
&lt;/table&gt;</code></pre>
<p>And especially useful when used in combination with prototype-only comment blocks:</p>
<pre class="html notranslate"><code>&lt;table&gt;
    &lt;!--/*/ &lt;th:block th:each="user : ${users}"&gt; /*/--&gt;
    &lt;tr&gt;
        &lt;td th:text="${user.login}"&gt;...&lt;/td&gt;
        &lt;td th:text="${user.name}"&gt;...&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td colspan="2" th:text="${user.address}"&gt;...&lt;/td&gt;
    &lt;/tr&gt;
    &lt;!--/*/ &lt;/th:block&gt; /*/--&gt;
&lt;/table&gt;</code></pre>
<p>Note how this solution allows templates to be valid HTML (no need to add forbidden <code class="notranslate">&lt;div&gt;</code> blocks inside <code class="notranslate">&lt;table&gt;</code>), and still works OK when open statically in browsers as prototypes!</p>
</section>
</section>
<section id="inlining" class="level1">
<h2 id="12-Inlining">12 Inlining</h2>
<section id="expression-inlining" class="level2">
<h2 id="12.1-Expression-inlining">12.1 Expression inlining</h2>
<p>Although the Standard Dialect allows us to do almost everything using tag attributes, there are situations in which we could prefer writing expressions directly into our HTML texts. For example, we could prefer writing this:</p>
<pre class="html notranslate"><code>&lt;p&gt;Hello, [[${session.user.name}]]!&lt;/p&gt;</code></pre>
<p>…instead of this:</p>
<pre class="html notranslate"><code>&lt;p&gt;Hello, &lt;span th:text="${session.user.name}"&gt;Sebastian&lt;/span&gt;!&lt;/p&gt;</code></pre>
<p>Expressions between <code class="notranslate">[[...]]</code> or <code class="notranslate">[(...)]</code> are considered <strong>inlined expressions</strong> in Thymeleaf, and inside them we can use any kind of expression that would also be valid in a <code class="notranslate">th:text</code> or <code class="notranslate">th:utext</code> attribute.</p>
<p>Note that, while <code class="notranslate">[[...]]</code> corresponds to <code class="notranslate">th:text</code> (i.e.&nbsp;result will be <em>HTML-escaped</em>), <code class="notranslate">[(...)]</code> corresponds to <code class="notranslate">th:utext</code> and will not perform any HTML-escaping. So with a variable such as <code class="notranslate">msg = 'This is &lt;b&gt;great!&lt;/b&gt;'</code>, given this fragment:</p>
<pre class="html notranslate"><code>&lt;p&gt;The message is "[(${msg})]"&lt;/p&gt;</code></pre>
<p>The result will have those <code class="notranslate">&lt;b&gt;</code> tags unescaped, so:</p>
<pre class="html notranslate"><code>&lt;p&gt;The message is "This is &lt;b&gt;great!&lt;/b&gt;"&lt;/p&gt;</code></pre>
<p>Whereas if escaped like:</p>
<pre class="html notranslate"><code>&lt;p&gt;The message is "[[${msg}]]"&lt;/p&gt;</code></pre>
<p>The result will be HTML-escaped:</p>
<pre class="html notranslate"><code>&lt;p&gt;The message is "This is &amp;lt;b&amp;gt;great!&amp;lt;/b&amp;gt;"&lt;/p&gt;</code></pre>
<p>Note that <strong>text inlining is active by default</strong> in the body of every tag in our markup –- not the tags themselves -–, so there is nothing we need to do to enable it.</p>
<section id="inlining-vs-natural-templates" class="level3">
<h3 id="Inlining-vs-natural-templates">Inlining vs natural templates</h3>
<p>If you come from other template engines in which this way of outputting text is the norm, you might be asking: <em>Why aren’t we doing this from the beginning? It’s less code than all those</em> <code class="notranslate">th:text</code> <em>attributes!</em></p>
<p>Well, be careful there, because although you might find inlining quite interesting, you should always remember that inlined expressions will be displayed verbatim in your HTML files when you open them statically, so you probably won’t be able to use them as design prototypes anymore!</p>
<p>The difference between how a browser would statically display our fragment of code without using inlining…</p>
<pre class="notranslate"><code>Hello, Sebastian!</code></pre>
<p>…and using it…</p>
<pre class="notranslate"><code>Hello, [[${session.user.name}]]!</code></pre>
<p>…is quite clear in terms of design usefulness.</p>
</section>
<section id="disabling-inlining" class="level3">
<h3 id="Disabling-inlining">Disabling inlining</h3>
<p>This mechanism can be disabled though, because there might actually be occasions in which we do want to output the <code class="notranslate">[[...]]</code> or <code class="notranslate">[(...)]</code> sequences without its contents being processed as an expression. For that, we will use <code class="notranslate">th:inline="none"</code>:</p>
<pre class="html notranslate"><code>&lt;p th:inline="none"&gt;A double array looks like this: [[1, 2, 3], [4, 5]]!&lt;/p&gt;</code></pre>
<p>This will result in:</p>
<pre class="html notranslate"><code>&lt;p&gt;A double array looks like this: [[1, 2, 3], [4, 5]]!&lt;/p&gt;</code></pre>
</section>
</section>
<section id="text-inlining" class="level2">
<h2 id="12.2-Text-inlining">12.2 Text inlining</h2>
<p><em>Text inlining</em> is very similar to the <em>expression inlining</em> capability we have just seen, but it actually adds more power. It has to be enabled explicitly with <code class="notranslate">th:inline="text"</code>.</p>
<p>Text inlining not only allows us to use the same <em>inlined expressions</em> we just saw, but in fact processes <em>tag bodies</em> as if they were templates processed in the <code class="notranslate">TEXT</code> template mode, which allows us to perform text-based template logic (not only output expressions).</p>
<p>We will see more about this in the next chapter about the <em>textual template modes</em>.</p>
</section>
<section id="javascript-inlining" class="level2">
<h2 id="12.3-JavaScript-inlining">12.3 JavaScript inlining</h2>
<p>JavaScript inlining allows for a better integration of JavaScript <code class="notranslate">&lt;script&gt;</code> blocks in templates being processed in the <code class="notranslate">HTML</code> template mode.</p>
<p>As with <em>text inlining</em>, this is actually equivalent to processing the scripts contents as if they were templates in the <code class="notranslate">JAVASCRIPT</code> template mode, and therefore all the power of the <em>textual template modes</em> (see next chapter) will be at hand. However, in this section we will focus on how we can use it for adding the output of our Thymeleaf expressions into our JavaScript blocks.</p>
<p>This mode has to be explicitly enabled using <code class="notranslate">th:inline="javascript"</code>:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = [[${session.user.name}]];
    ...
&lt;/script&gt;</code></pre>
<p>This will result in:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = "Sebastian \"Fruity\" Applejuice";
    ...
&lt;/script&gt;</code></pre>
<p>Two important things to note in the code above:</p>
<p><em>First</em>, that JavaScript inlining will not only output the required text, but also enclose it with quotes and JavaScript-escape its contents, so that the expression results are output as a <strong>well-formed JavaScript literal</strong>.</p>
<p><em>Second</em>, that this is happening because we are outputting the <code class="notranslate">${session.user.name}</code> expression as <strong>escaped</strong>, i.e.&nbsp;using a double-bracket expression: <code class="notranslate">[[${session.user.name}]]</code>. If instead we used <em>unescaped</em> like:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = [(${session.user.name})];
    ...
&lt;/script&gt;</code></pre>
<p>The result would look like:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = Sebastian "Fruity" Applejuice;
    ...
&lt;/script&gt;</code></pre>
<p>…which is malformed JavaScript code. But outputting something unescaped might be what we need if we are building parts of our script by means of appending inlined expressions, so it’s good to have this tool at hand.</p>
<section id="javascript-natural-templates" class="level3">
<h3 id="JavaScript-natural-templates">JavaScript natural templates</h3>
<p>The mentioned <em>intelligence</em> of the JavaScript inlining mechanism goes much further than just applying JavaScript-specific escaping and outputting expression results as valid literals.</p>
<p>For example, we can wrap our (escaped) inlined expressions in JavaScript comments like:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
    ...
&lt;/script&gt;</code></pre>
<p>And Thymeleaf will ignore everything we have written <em>after the comment and before the semicolon</em> (in this case <code class="notranslate">'Gertrud Kiwifruit'</code>), so the result of executing this will look exactly like when we were not using the wrapping comments:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = "Sebastian \"Fruity\" Applejuice";
    ...
&lt;/script&gt;</code></pre>
<p>But have another careful look at the original template code:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
    ...
&lt;/script&gt;</code></pre>
<p>Note how this is <strong>valid JavaScript</strong> code. And it will perfectly execute when you open your template file in a static manner (without executing it at a server).</p>
<p>So what we have here is a way to do <strong>JavaScript natural templates</strong>!</p>
</section>
<section id="advanced-inlined-evaluation-and-javascript-serialization" class="level3">
<h3 id="Advanced-inlined-evaluation-and-JavaScript-serialization">Advanced inlined evaluation and JavaScript serialization</h3>
<p>An important thing to note regarding JavaScript inlining is that this expression evaluation is intelligent and not limited to Strings. Thymeleaf will correctly write in JavaScript syntax the following kinds of objects:</p>
<ul>
<li>Strings</li>
<li>Numbers</li>
<li>Booleans</li>
<li>Arrays</li>
<li>Collections</li>
<li>Maps</li>
<li>Beans (objects with <em>getter</em> and <em>setter</em> methods)</li>
</ul>
<p>For example, if we had the following code:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var user = /*[[${session.user}]]*/ null;
    ...
&lt;/script&gt;</code></pre>
<p>That <code class="notranslate">${session.user}</code> expression will evaluate to a <code class="notranslate">User</code> object, and Thymeleaf will correctly convert it to Javascript syntax:</p>
<pre class="html notranslate"><code>&lt;script th:inline="javascript"&gt;
    ...
    var user = {"age":null,"firstName":"John","lastName":"Apricot",
                "name":"John Apricot","nationality":"Antarctica"};
    ...
&lt;/script&gt;</code></pre>
<p>The way this JavaScript serialization is done is by means of an implementation of the <code class="notranslate">org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer</code> interface, which can be configured at the instance of the <code class="notranslate">StandardDialect</code> being used at the template engine.</p>
<p>The default implementation of this JS serialization mechanism will look for the <a href="javascript:window.open('https://github.com/FasterXML/jackson');" target="_blank" rel="noopener noreferrer">Jackson library <i class="fa fa-external-link"></i></a> in the classpath and, if present, will use it. If not, it will apply a built-in serialization mechanism that covers the needs of most scenarios and produces similar results (but is less flexible).</p>
</section>
</section>
<section id="css-inlining" class="level2">
<h2 id="12.4-CSS-inlining">12.4 CSS inlining</h2>
<p>Thymeleaf also allows the use of inlining in CSS <code class="notranslate">&lt;style&gt;</code> tags, such as:</p>
<pre class="html notranslate"><code>&lt;style th:inline="css"&gt;
  ...
&lt;/style&gt;</code></pre>
<p>For example, say we have two variables set to two different <code class="notranslate">String</code> values:</p>
<pre class="notranslate"><code>classname = 'main elems'
align = 'center'</code></pre>
<p>We could use them just like:</p>
<pre class="html notranslate"><code>&lt;style th:inline="css"&gt;
    .[[${classname}]] {
      text-align: [[${align}]];
    }
&lt;/style&gt;</code></pre>
<p>And the result would be:</p>
<pre class="html notranslate"><code>&lt;style th:inline="css"&gt;
    .main\ elems {
      text-align: center;
    }
&lt;/style&gt;</code></pre>
<p>Note how CSS inlining also bears some <em>intelligence</em>, just like JavaScript’s. Specifically, expressions output via <em>escaped</em> expressions like <code class="notranslate">[[${classname}]]</code> will be escaped as <strong>CSS identifiers</strong>. That is why our <code class="notranslate">classname = 'main elems'</code> has turned into <code class="notranslate">main\ elems</code> in the fragment of code above.</p>
<section id="advanced-features-css-natural-templates-etc." class="level3">
<h3 id="Advanced-features:-CSS-natural-templates,-etc.">Advanced features: CSS natural templates, etc.</h3>
<p>In an equivalent way to what was explained before for JavaScript, CSS inlining also allows for our <code class="notranslate">&lt;style&gt;</code> tags to work both statically and dynamically, i.e.&nbsp;as <strong>CSS natural templates</strong> by means of wrapping inlined expressions in comments. See:</p>
<pre class="html notranslate"><code>&lt;style th:inline="css"&gt;
    .main\ elems {
      text-align: /*[[${align}]]*/ left;
    }
&lt;/style&gt;</code></pre>
</section>
</section>
</section>
<section id="textual-template-modes" class="level1">
<h2 id="13-Textual-template-modes">13 Textual template modes</h2>
<section id="textual-syntax" class="level2">
<h2 id="13.1-Textual-syntax">13.1 Textual syntax</h2>
<p>Three of the Thymeleaf <em>template modes</em> are considered <strong>textual</strong>: <code class="notranslate">TEXT</code>, <code class="notranslate">JAVASCRIPT</code> and <code class="notranslate">CSS</code>. This differentiates them from the markup template modes: <code class="notranslate">HTML</code> and <code class="notranslate">XML</code>.</p>
<p>The key difference between <em>textual</em> template modes and the markup ones is that in a textual template there are no tags into which to insert logic in the form of attributes, so we have to rely on other mechanisms.</p>
<p>The first and most basic of these mechanisms is <strong>inlining</strong>, which we have already detailed in the previous chapter. Inlining syntax is the most simple way to output results of expressions in textual template mode, so this is a perfectly valid template for a text email. </p>
<pre class="notranslate"><code>  Dear [(${name})],

  Please find attached the results of the report you requested
  with name "[(${report.name})]".

  Sincerely,
    The Reporter.</code></pre>
<p>Even without tags, the example above is a complete and valid Thymeleaf template that can be executed in the <code class="notranslate">TEXT</code> template mode.</p>
<p>But in order to include more complex logic than mere <em>output expressions</em>, we need a new non-tag-based syntax:</p>
<pre class="notranslate"><code>[# th:each="item : ${items}"]
  - [(${item})]
[/]</code></pre>
<p>Which is actually the <em>condensed</em> version of the more verbose:</p>
<pre class="notranslate"><code>[#th:block th:each="item : ${items}"]
  - [#th:block th:utext="${item}" /]
[/th:block]</code></pre>
<p>Note how this new syntax is based on elements (i.e.&nbsp;processable tags) that are declared as <code class="notranslate">[#element ...]</code> instead of <code class="notranslate">&lt;element ...&gt;</code>. Elements are open like <code class="notranslate">[#element ...]</code> and closed like <code class="notranslate">[/element]</code>, and standalone tags can be declared by minimizing the open element with a <code class="notranslate">/</code> in a way almost equivalent to XML tags: <code class="notranslate">[#element ... /]</code>.</p>
<p>The Standard Dialect only contains a processor for one of these elements: the already-known <code class="notranslate">th:block</code>, though we could extend this in our dialects and create new elements in the usual way. Also, the <code class="notranslate">th:block</code> element (<code class="notranslate">[#th:block ...] ... [/th:block]</code>) is allowed to be abbreviated as the empty string (<code class="notranslate">[# ...] ... [/]</code>), so the above block is actually equivalent to:</p>
<pre class="notranslate"><code>[# th:each="item : ${items}"]
  - [# th:utext="${item}" /]
[/]</code></pre>
<p>And given <code class="notranslate">[# th:utext="${item}" /]</code> is equivalent to an <em>inlined unescaped expression</em>, we could just use it in order to have less code. Thus we end up with the first fragment of code we saw above:</p>
<pre class="notranslate"><code>[# th:each="item : ${items}"]
  - [(${item})]
[/]</code></pre>
<p>Note that the <em>textual syntax requires full element balance (no unclosed tags) and quoted attributes</em> – it’s more XML-style than HTML-style.</p>
<p>Let’s have a look at a more complete example of a <code class="notranslate">TEXT</code> template, a <em>plain text</em> email template:</p>
<pre class="notranslate"><code>Dear [(${customer.name})],

This is the list of our products:

[# th:each="prod : ${products}"]
   - [(${prod.name})]. Price: [(${prod.price})] EUR/kg
[/]

Thanks,
  The Thymeleaf Shop</code></pre>
<p>After executing, the result of this could be something like:</p>
<pre class="notranslate"><code>Dear Mary Ann Blueberry,

This is the list of our products:

   - Apricots. Price: 1.12 EUR/kg
   - Bananas. Price: 1.78 EUR/kg
   - Apples. Price: 0.85 EUR/kg
   - Watermelon. Price: 1.91 EUR/kg

Thanks,
  The Thymeleaf Shop</code></pre>
<p>And another example in <code class="notranslate">JAVASCRIPT</code> template mode, a <code class="notranslate">greeter.js</code> file, we process as a textual template and which result we call from our HTML pages. Note this is <em>not</em> a <code class="notranslate">&lt;script&gt;</code> block in an HTML template, but a <code class="notranslate">.js</code> file being processed as a template on its own:</p>
<pre class="javascript notranslate"><code>var greeter = function() {

    var username = [[${session.user.name}]];

    [# th:each="salut : ${salutations}"]    
      alert([[${salut}]] + " " + username);
    [/]

};</code></pre>
<p>After executing, the result of this could be something like:</p>
<pre class="javascript notranslate"><code>var greeter = function() {

    var username = "Bertrand \"Crunchy\" Pear";

      alert("Hello" + " " + username);
      alert("Ol\u00E1" + " " + username);
      alert("Hola" + " " + username);

};</code></pre>
<section id="escaped-element-attributes" class="level3">
<h3 id="Escaped-element-attributes">Escaped element attributes</h3>
<p>In order to avoid interactions with parts of the template that might be processed in other modes (e.g. <code class="notranslate">text</code>-mode inlining inside an <code class="notranslate">HTML</code> template), Thymeleaf 3.0 allows the attributes in elements in its <em>textual syntax</em> to be escaped. So:</p>
<ul>
<li>Attributes in <code class="notranslate">TEXT</code> template mode will be <em>HTML-unescaped</em>.</li>
<li>Attributes in <code class="notranslate">JAVASCRIPT</code> template mode will be <em>JavaScript-unescaped</em>.</li>
<li>Attributes in <code class="notranslate">CSS</code> template mode will be <em>CSS-unescaped</em>.</li>
</ul>
<p>So this would be perfectly OK in a <code class="notranslate">TEXT</code>-mode template (note the <code class="notranslate">&amp;gt;</code>):</p>
<pre class="notranslate"><code>  [# th:if="${120&amp;lt;user.age}"]
     Congratulations!
  [/]</code></pre>
<p>Of course that <code class="notranslate">&amp;lt;</code> would make no sense in a <em>real text</em> template, but it is a good idea if we are processing an HTML template with a <code class="notranslate">th:inline="text"</code> block containing the code above and we want to make sure our browser doesn’t take that <code class="notranslate">&lt;user.age</code> for the name of an open tag when statically opening the file as a prototype.</p>
</section>
</section>
<section id="extensibility" class="level2">
<h2 id="13.2-Extensibility">13.2 Extensibility</h2>
<p>One of the advantages of this syntax is that it is just as extensible as the <em>markup</em> one. Developers can still define their own dialects with custom elements and attributes, apply a prefix to them (optionally), and then use them in textual template modes:</p>
<pre class="notranslate"><code>  [#myorg:dosomething myorg:importantattr="211"]some text[/myorg:dosomething]</code></pre>
</section>
<section id="textual-prototype-only-comment-blocks-adding-code" class="level2">
<h2 id="13.3-Textual-prototype-only-comment-blocks:-adding-code">13.3 Textual prototype-only comment blocks: adding code</h2>
<p>The <code class="notranslate">JAVASCRIPT</code> and <code class="notranslate">CSS</code> template modes (not available for <code class="notranslate">TEXT</code>) allow including code between a special comment syntax <code class="notranslate">/*[+...+]*/</code> so that Thymeleaf will automatically uncomment such code when processing the template:</p>
<pre class="javascript notranslate"><code>var x = 23;

/*[+

var msg  = "This is a working application";

+]*/

var f = function() {
    ...</code></pre>
<p>Will be executed as:</p>
<pre class="javascript notranslate"><code>var x = 23;

var msg  = "This is a working application";

var f = function() {
...</code></pre>
<p>You can include expressions inside these comments, and they will be evaluated:</p>
<pre class="javascript notranslate"><code>var x = 23;

/*[+

var msg  = "Hello, " + [[${session.user.name}]];

+]*/

var f = function() {
...</code></pre>
</section>
<section id="textual-parser-level-comment-blocks-removing-code" class="level2">
<h2 id="13.4-Textual-parser-level-comment-blocks:-removing-code">13.4 Textual parser-level comment blocks: removing code</h2>
<p>In a way similar to that of prototype-only comment blocks, all the three textual template modes (<code class="notranslate">TEXT</code>, <code class="notranslate">JAVASCRIPT</code> and <code class="notranslate">CSS</code>) make it possible to instruct Thymeleaf to remove code between special <code class="notranslate">/*[- */</code> and <code class="notranslate">/* -]*/</code> marks, like this:</p>
<pre class="javascript notranslate"><code>var x = 23;

/*[- */

var msg  = "This is shown only when executed statically!";

/* -]*/

var f = function() {
...</code></pre>
<p>Or this, in <code class="notranslate">TEXT</code> mode:</p>
<pre class="notranslate"><code>...
/*[- Note the user is obtained from the session, which must exist -]*/
Welcome [(${session.user.name})]!
...</code></pre>
</section>
<section id="natural-javascript-and-css-templates" class="level2">
<h2 id="13.5-Natural-JavaScript-and-CSS-templates">13.5 Natural JavaScript and CSS templates</h2>
<p>As seen in the previous chapter, JavaScript and CSS inlining offer the possibility to include inlined expressions inside JavaScript/CSS comments, like:</p>
<pre class="javascript notranslate"><code>...
var username = /*[[${session.user.name}]]*/ "Sebastian Lychee";
...</code></pre>
<p>…which is valid JavaScript, and once executed could look like:</p>
<pre class="html notranslate"><code>...
var username = "John Apricot";
...</code></pre>
<p>This same <em>trick</em> of enclosing inlined expressions inside comments can in fact be used for the entire textual mode syntax:</p>
<pre class="notranslate"><code>  /*[# th:if="${user.admin}"]*/
     alert('Welcome admin');
  /*[/]*/</code></pre>
<p>That alert in the code above will be shown when the template is open statically – because it is 100% valid JavaScript –, and also when the template is run if the user is an admin. It is equivalent to:</p>
<pre class="notranslate"><code>  [# th:if="${user.admin}"]
     alert('Welcome admin');
  [/]</code></pre>
<p>…which is actually the code to which the initial version is converted during template parsing. </p>
<p>Note however that wrapping elements in comments does not clean the lines they live in (to the right until a <code class="notranslate">;</code> is found) as inlined output expressions do. That behaviour is reserved for inlined output expressions only.</p>
<p>So Thymeleaf 3.0 allows the development of <strong>complex JavaScript scripts and CSS style sheets in the form of natural templates</strong>, valid both as a <em>prototype</em> and as a <em>working template</em>.</p>
</section>
</section>
<section id="some-more-pages-for-our-grocery" class="level1">
<h2 id="14-Some-more-pages-for-our-grocery">14 Some more pages for our grocery</h2>
<p>Now we know a lot about using Thymeleaf, we can add some new pages to our website for order management.</p>
<p>Note that we will focus on HTML code, but you can have a look at the bundled source code if you want to see the corresponding controllers.</p>
<section id="order-list" class="level2">
<h2 id="14.1-Order-List">14.1 Order List</h2>
<p>Let’s start by creating an order list page, <code class="notranslate">/WEB-INF/templates/order/list.html</code>:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html xmlns:th="http://www.thymeleaf.org"&gt;

  &lt;head&gt;

    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" 
          href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" /&gt;
  &lt;/head&gt;

  &lt;body&gt;

    &lt;h1&gt;Order list&lt;/h1&gt;
  
    &lt;table&gt;
      &lt;tr&gt;
        &lt;th&gt;DATE&lt;/th&gt;
        &lt;th&gt;CUSTOMER&lt;/th&gt;
        &lt;th&gt;TOTAL&lt;/th&gt;
        &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each="o : ${orders}" th:class="${oStat.odd}? 'odd'"&gt;
        &lt;td th:text="${#calendars.format(o.date,'dd/MMM/yyyy')}"&gt;13 jan 2011&lt;/td&gt;
        &lt;td th:text="${o.customer.name}"&gt;Frederic Tomato&lt;/td&gt;
        &lt;td th:text="${#aggregates.sum(o.orderLines.{purchasePrice * amount})}"&gt;23.32&lt;/td&gt;
        &lt;td&gt;
          &lt;a href="details.html" th:href="@{/order/details(orderId=${o.id})}"&gt;view&lt;/a&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
  
    &lt;p&gt;
      &lt;a href="../home.html" th:href="@{/}"&gt;Return to home&lt;/a&gt;
    &lt;/p&gt;
    
  &lt;/body&gt;
  
&lt;/html&gt;</code></pre>
<p>There’s nothing here that should surprise us, except for this little bit of OGNL magic:</p>
<pre class="html notranslate"><code>&lt;td th:text="${#aggregates.sum(o.orderLines.{purchasePrice * amount})}"&gt;23.32&lt;/td&gt;</code></pre>
<p>What that does is, for each order line (<code class="notranslate">OrderLine</code> object) in the order, multiply its <code class="notranslate">purchasePrice</code> and <code class="notranslate">amount</code> properties (by calling the corresponding <code class="notranslate">getPurchasePrice()</code> and <code class="notranslate">getAmount()</code> methods) and return the result into a list of numbers, later aggregated by the <code class="notranslate">#aggregates.sum(...)</code> function in order to obtain the order total price.</p>
<p>You’ve got to love the power of OGNL.</p>
</section>
<section id="order-details" class="level2">
<h2 id="14.2-Order-Details">14.2 Order Details</h2>
<p>Now for the order details page, in which we will make a heavy use of asterisk syntax:</p>
<pre class="html notranslate"><code>&lt;!DOCTYPE html&gt;

&lt;html xmlns:th="http://www.thymeleaf.org"&gt;

  &lt;head&gt;
    &lt;title&gt;Good Thymes Virtual Grocery&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
    &lt;link rel="stylesheet" type="text/css" media="all" 
          href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" /&gt;
  &lt;/head&gt;

  &lt;body th:object="${order}"&gt;

    &lt;h1&gt;Order details&lt;/h1&gt;

    &lt;div&gt;
      &lt;p&gt;&lt;b&gt;Code:&lt;/b&gt; &lt;span th:text="*{id}"&gt;99&lt;/span&gt;&lt;/p&gt;
      &lt;p&gt;
        &lt;b&gt;Date:&lt;/b&gt;
        &lt;span th:text="*{#calendars.format(date,'dd MMM yyyy')}"&gt;13 jan 2011&lt;/span&gt;
      &lt;/p&gt;
    &lt;/div&gt;

    &lt;h2&gt;Customer&lt;/h2&gt;

    &lt;div th:object="*{customer}"&gt;
      &lt;p&gt;&lt;b&gt;Name:&lt;/b&gt; &lt;span th:text="*{name}"&gt;Frederic Tomato&lt;/span&gt;&lt;/p&gt;
      &lt;p&gt;
        &lt;b&gt;Since:&lt;/b&gt;
        &lt;span th:text="*{#calendars.format(customerSince,'dd MMM yyyy')}"&gt;1 jan 2011&lt;/span&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  
    &lt;h2&gt;Products&lt;/h2&gt;
  
    &lt;table&gt;
      &lt;tr&gt;
        &lt;th&gt;PRODUCT&lt;/th&gt;
        &lt;th&gt;AMOUNT&lt;/th&gt;
        &lt;th&gt;PURCHASE PRICE&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr th:each="ol,row : *{orderLines}" th:class="${row.odd}? 'odd'"&gt;
        &lt;td th:text="${ol.product.name}"&gt;Strawberries&lt;/td&gt;
        &lt;td th:text="${ol.amount}" class="number"&gt;3&lt;/td&gt;
        &lt;td th:text="${ol.purchasePrice}" class="number"&gt;23.32&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;

    &lt;div&gt;
      &lt;b&gt;TOTAL:&lt;/b&gt;
      &lt;span th:text="*{#aggregates.sum(orderLines.{purchasePrice * amount})}"&gt;35.23&lt;/span&gt;
    &lt;/div&gt;
  
    &lt;p&gt;
      &lt;a href="list.html" th:href="@{/order/list}"&gt;Return to order list&lt;/a&gt;
    &lt;/p&gt;

  &lt;/body&gt;
  
&lt;/html&gt;</code></pre>
<p>Not much really new here, except for this nested object selection:</p>
<pre class="html notranslate"><code>&lt;body th:object="${order}"&gt;

  ...

  &lt;div th:object="*{customer}"&gt;
    &lt;p&gt;&lt;b&gt;Name:&lt;/b&gt; &lt;span th:text="*{name}"&gt;Frederic Tomato&lt;/span&gt;&lt;/p&gt;
    ...
  &lt;/div&gt;

  ...
&lt;/body&gt;</code></pre>
<p>…which makes that <code class="notranslate">*{name}</code> equivalent to:</p>
<pre class="html notranslate"><code>&lt;p&gt;&lt;b&gt;Name:&lt;/b&gt; &lt;span th:text="${order.customer.name}"&gt;Frederic Tomato&lt;/span&gt;&lt;/p&gt;</code></pre>
</section>
</section>
<section id="more-on-configuration" class="level1">
<h2 id="15-More-on-Configuration">15 More on Configuration</h2>
<section id="template-resolvers" class="level2">
<h2 id="15.1-Template-Resolvers">15.1 Template Resolvers</h2>
<p>For our Good Thymes Virtual Grocery, we chose an <code class="notranslate">ITemplateResolver</code> implementation called <code class="notranslate">ServletContextTemplateResolver</code> that allowed us to obtain templates as resources from the Servlet Context.</p>
<p>Besides giving us the ability to create our own template resolver by implementing <code class="notranslate">ITemplateResolver,</code> Thymeleaf includes four implementations out of the box:</p>
<ul>
<li> <p><code class="notranslate">org.thymeleaf.templateresolver.ClassLoaderTemplateResolver</code>, which resolves templates as classloader resources, like:</p> <pre class="java notranslate"><code>return Thread.currentThread().getContextClassLoader().getResourceAsStream(template);</code></pre> </li>
<li> <p><code class="notranslate">org.thymeleaf.templateresolver.FileTemplateResolver</code>, which resolves templates as files from the file system, like:</p> <pre class="java notranslate"><code>return new FileInputStream(new File(template));</code></pre> </li>
<li> <p><code class="notranslate">org.thymeleaf.templateresolver.UrlTemplateResolver</code>, which resolves templates as URLs (even non-local ones), like:</p> <pre class="java notranslate"><code>return (new URL(template)).openStream();</code></pre> </li>
<li> <p><code class="notranslate">org.thymeleaf.templateresolver.StringTemplateResolver</code>, which resolves templates directly as the <code class="notranslate">String</code> being specified as <code class="notranslate">template</code> (or <em>template name</em>, which in this case is obviously much more than a mere name): </p> <pre class="java notranslate"><code>return new StringReader(templateName);</code></pre> </li>
</ul>
<p>All of the pre-bundled implementations of <code class="notranslate">ITemplateResolver</code> allow the same set of configuration parameters, which include:</p>
<ul>
<li> <p>Prefix and suffix (as already seen):</p> <pre class="java notranslate"><code>templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");</code></pre> </li>
<li> <p>Template aliases that allow the use of template names that do not directly correspond to file names. If both suffix/prefix and alias exist, alias will be applied before prefix/suffix:</p> <pre class="java notranslate"><code>templateResolver.addTemplateAlias("adminHome","profiles/admin/home");
templateResolver.setTemplateAliases(aliasesMap);</code></pre> </li>
<li> <p>Encoding to be applied when reading templates:</p> <pre class="java notranslate"><code>templateResolver.setCharacterEncoding("UTF-8");</code></pre> </li>
<li> <p>Template mode to be used:</p> <pre class="java notranslate"><code>// Default is HTML
templateResolver.setTemplateMode("XML");</code></pre> </li>
<li> <p>Default mode for template cache, and patterns for defining whether specific templates are cacheable or not:</p> <pre class="java notranslate"><code>// Default is true
templateResolver.setCacheable(false);
t</code></pre></li>
</ul>
</section>
</section></div>
</div>
</section>
<div class="right-sidebar">
<div class="affix"><ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-6108808167664152" data-ad-slot="3102929424" data-ad-format="auto" data-full-width-responsive="true"></ins>
<script data-cfasync="false" src="static/js/email-decode.min.js"></script><script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
</div>
</div>
<div class="ft">
<a href="#toolbar-title" id="anchorNavigationExGoTop"><i class="fa fa-arrow-up"></i></a>
<footer class="footer">
<div class="footer__container--normal">
<img alt="扫码关注公众号" title="扫码关注公众号" src="static/picture/qr-code.png" width="170" height="170">
<div class="footer__description--normal">
<p class="paragraph footer__author--normal">Docs4dev<sup class="super">&#xAE;</sup>
</p>
<p class="paragraph footer__quote&#45;&#45;normal">
如果你在使用过程中遇到任何问题，可以在 <a href="javascript:window.open('https://github.com/docs4dev/docs4dev-issues');" target="_blank" rel="noopener noreferrer">这里<i class="fa fa-external-link"></i></a> 提issue。
</p>
<div class="footer__main--normal">
<p class="paragraph footer__main__paragraph--normal copyright" style="color: #666 !important;">
<a href="javascript:window.open('https://beian.miit.gov.cn/');">
蜀ICP备14021783号-6
</a>
</p>
<p class="paragraph footer__main__paragraph--normal copyright" style="color: #666 !important;">
Copyright &#xA9; Docs4dev all
right reserved, powered by <a href="index2.html" target="_blank">Docs4dev</a></p>
</div>
</div>
</div>
<div class="box__issues">
</div>
</footer>
</div>
</div>
</div>
</div>
</div>
<script>
  var hasToc = true;
  /*  var downloadable = /!*false*!/ false;
    var editable = /!*false*!/ false;
    var code = /!*"thymeleaf"*!/ false;
    var version = /!*"3.0"*!/ false;
    var type = /!*"reference"*!/ false;
    var lang = /!*"en"*!/ 'en';
    //edit link
    require(["gitbook", "jQuery"], function (gitbook, $) {
      gitbook.events.bind('start', function (e, config) {
        // Add edit toolbar to left
        var chapterId = /!*24802*!/ 0;
        if (downloadable) {
          gitbook.toolbar.createButton({
            icon: 'fa fa-download',
            text: '下载',
            onClick: function () {
              window.open('/download?code=' + code + '&version=' + version + '&type=' + type + '&lang=' + lang);
            }
          });
        }
        if (editable) {
          gitbook.toolbar.createButton({
            icon: 'fa fa-edit',
            text: '编辑此页',
            onClick: function () {
              window.open('/docs/edit?chapterId=' + chapterId + '&lang=' + lang);
            }
          });
        }
      });
    });*/
</script>
<script>
    var code = "thymeleaf";
    var lang = "en";
  </script>

<script src="static/js/autocomplete-js.js"></script>
<script src="static/js/app.min.js"></script>
<script src="static/js/search.min.js"></script>
</body>
</html>
